sndev cleanup (#1563)
* begin sndev cleanup * cleanup sndev * fix posix shell compliance * add tests to sndev
This commit is contained in:
		
							parent
							
								
									c31cf97288
								
							
						
					
					
						commit
						4675a2c29d
					
				@ -138,13 +138,13 @@ LND_PUBKEY=02cb2e2d5a6c5b17fa67b1a883e2973c82e328fb9bd08b2b156a9e23820c87a490
 | 
			
		||||
# stacker lnd container stuff
 | 
			
		||||
STACKER_LND_REST_PORT=8081
 | 
			
		||||
STACKER_LND_GRPC_PORT=10010
 | 
			
		||||
# docker exec -u lnd stacker_lnd lncli newaddress p2wkh --unused
 | 
			
		||||
# docker exec -u lnd lnd lncli newaddress p2wkh --unused
 | 
			
		||||
STACKER_LND_ADDR=bcrt1qfqau4ug9e6rtrvxrgclg58e0r93wshucumm9vu
 | 
			
		||||
STACKER_LND_PUBKEY=028093ae52e011d45b3e67f2e0f2cb6c3a1d7f88d2920d408f3ac6db3a56dc4b35
 | 
			
		||||
 | 
			
		||||
# stacker cln container stuff
 | 
			
		||||
STACKER_CLN_REST_PORT=9092
 | 
			
		||||
# docker exec -u clightning stacker_cln lightning-cli newaddr bech32
 | 
			
		||||
# docker exec -u clightning cln lightning-cli newaddr bech32
 | 
			
		||||
STACKER_CLN_ADDR=bcrt1q02sqd74l4pxedy24fg0qtjz4y2jq7x4lxlgzrx
 | 
			
		||||
STACKER_CLN_PUBKEY=03ca7acec181dbf5e427c682c4261a46a0dd9ea5f35d97acb094e399f727835b90
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										25
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								README.md
									
									
									
									
									
								
							@ -76,16 +76,12 @@ COMMANDS
 | 
			
		||||
 | 
			
		||||
  sn:
 | 
			
		||||
    login                 login as a nym
 | 
			
		||||
    fund_user     fund a nym without using an LN invoice
 | 
			
		||||
    set_balance           set the balance of a nym
 | 
			
		||||
 | 
			
		||||
  lnd:
 | 
			
		||||
  lightning:
 | 
			
		||||
    fund                   pay a bolt11 for funding
 | 
			
		||||
    withdraw               create a bolt11 for withdrawal
 | 
			
		||||
 | 
			
		||||
  cln:
 | 
			
		||||
    cln_fund      pay a bolt11 for funding with CLN
 | 
			
		||||
    cln_withdraw  create a bolt11 for withdrawal with CLN
 | 
			
		||||
 | 
			
		||||
  db:
 | 
			
		||||
    psql                   open psql on db
 | 
			
		||||
    prisma                 run prisma commands
 | 
			
		||||
@ -93,15 +89,14 @@ COMMANDS
 | 
			
		||||
  dev:
 | 
			
		||||
    pr                     fetch and checkout a pr
 | 
			
		||||
    lint                   run linters
 | 
			
		||||
    open          open container url in browser
 | 
			
		||||
    test                   run tests
 | 
			
		||||
 | 
			
		||||
  other:
 | 
			
		||||
    cli                    service cli passthrough
 | 
			
		||||
    open                   open service GUI in browser
 | 
			
		||||
    onion                  service onion address
 | 
			
		||||
    cert                   service tls cert
 | 
			
		||||
    compose                docker compose passthrough
 | 
			
		||||
    sn_lndcli       lncli passthrough on sn_lnd
 | 
			
		||||
    stacker_lndcli  lncli passthrough on stacker_lnd
 | 
			
		||||
    stacker_clncli  lightning-cli passthrough on stacker_cln
 | 
			
		||||
    stacker_litcli  litcli passthrough on litd
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Modifying services
 | 
			
		||||
@ -368,9 +363,11 @@ You can connect to the local database via `./sndev psql`. [psql](https://www.pos
 | 
			
		||||
 | 
			
		||||
<br>
 | 
			
		||||
 | 
			
		||||
## Running lncli on the local lnd nodes
 | 
			
		||||
## Running cli on local lightning nodes
 | 
			
		||||
 | 
			
		||||
You can run `lncli` on the local lnd nodes via `./sndev sn_lncli` and `./sndev stacker_lncli`. The node for your local SN instance is `sn_lnd` and the node serving as any external node, like a stacker's node or external wallet, is `stacker_lnd`.
 | 
			
		||||
You can run `lncli` on the local lnd nodes via `./sndev cli lnd` and `./sndev cli sn_lnd`. The node for your local SN instance is `sn_lnd` and the node serving as any external node, like a stacker's node or external wallet, is `lnd`.
 | 
			
		||||
 | 
			
		||||
You can run `lightning-cli` on the local cln node via `./sndev cli cln` which serves as an external node or wallet.
 | 
			
		||||
 | 
			
		||||
<br>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -275,7 +275,7 @@ services:
 | 
			
		||||
            echo "Creating wallet and address..."
 | 
			
		||||
            bitcoin-cli createwallet ""
 | 
			
		||||
            nodes+=($$(bitcoin-cli getnewaddress))
 | 
			
		||||
            echo "Mining 100 blocks to sn_lnd, stacker_lnd, stacker_cln..."
 | 
			
		||||
            echo "Mining 100 blocks to sn_lnd, lnd, cln..."
 | 
			
		||||
            for addr in  "$${nodes[@]}"; do
 | 
			
		||||
              bitcoin-cli generatetoaddress 100 $$addr
 | 
			
		||||
              echo "Mining 100 blocks to a random address..."
 | 
			
		||||
@ -349,6 +349,8 @@ services:
 | 
			
		||||
    volumes:
 | 
			
		||||
      - sn_lnd:/home/lnd/.lnd
 | 
			
		||||
    labels:
 | 
			
		||||
      CLI: "lncli"
 | 
			
		||||
      CLI_USER: "lnd"
 | 
			
		||||
      ofelia.enabled: "true"
 | 
			
		||||
      ofelia.job-exec.sn_channel_cron.schedule: "@every 1m"
 | 
			
		||||
      ofelia.job-exec.sn_channel_cron.command: >
 | 
			
		||||
@ -356,17 +358,17 @@ services:
 | 
			
		||||
          if [ $$(lncli getinfo | jq '.num_active_channels + .num_pending_channels') -ge 3 ]; then
 | 
			
		||||
            exit 0
 | 
			
		||||
          else
 | 
			
		||||
            lncli openchannel --node_key=$STACKER_LND_PUBKEY --connect stacker_lnd:9735 --sat_per_vbyte 1 \\
 | 
			
		||||
            lncli openchannel --node_key=$STACKER_LND_PUBKEY --connect lnd:9735 --sat_per_vbyte 1 \\
 | 
			
		||||
              --min_confs 0 --local_amt=1000000000 --push_amt=500000000
 | 
			
		||||
          fi
 | 
			
		||||
        "
 | 
			
		||||
    cpu_shares: "${CPU_SHARES_MODERATE}"
 | 
			
		||||
  stacker_lnd:
 | 
			
		||||
  lnd:
 | 
			
		||||
    build:
 | 
			
		||||
      context: ./docker/lnd
 | 
			
		||||
      args:
 | 
			
		||||
        - LN_NODE_FOR=stacker
 | 
			
		||||
    container_name: stacker_lnd
 | 
			
		||||
    container_name: lnd
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    profiles:
 | 
			
		||||
      - payments
 | 
			
		||||
@ -384,9 +386,9 @@ services:
 | 
			
		||||
      - 'lnd'
 | 
			
		||||
      - '--noseedbackup'
 | 
			
		||||
      - '--trickledelay=5000'
 | 
			
		||||
      - '--alias=stacker_lnd'
 | 
			
		||||
      - '--externalip=stacker_lnd'
 | 
			
		||||
      - '--tlsextradomain=stacker_lnd'
 | 
			
		||||
      - '--alias=lnd'
 | 
			
		||||
      - '--externalip=lnd'
 | 
			
		||||
      - '--tlsextradomain=lnd'
 | 
			
		||||
      - '--tlsextradomain=host.docker.internal'
 | 
			
		||||
      - '--tlsextradomain=$${ONION_DOMAIN}'
 | 
			
		||||
      - '--listen=0.0.0.0:9735'
 | 
			
		||||
@ -413,12 +415,16 @@ services:
 | 
			
		||||
      - "${STACKER_LND_REST_PORT}:8080"
 | 
			
		||||
      - "${STACKER_LND_GRPC_PORT}:10009"
 | 
			
		||||
    volumes:
 | 
			
		||||
      - stacker_lnd:/home/lnd/.lnd
 | 
			
		||||
      - lnd:/home/lnd/.lnd
 | 
			
		||||
      - tordata:/home/lnd/.tor
 | 
			
		||||
    labels:
 | 
			
		||||
      TORDIR: "/home/lnd/.tor"
 | 
			
		||||
      CERTDIR: "/home/lnd/.lnd"
 | 
			
		||||
      CLI: "lncli"
 | 
			
		||||
      CLI_USER: "lnd"
 | 
			
		||||
      ofelia.enabled: "true"
 | 
			
		||||
      ofelia.job-exec.stacker_lnd_channel_cron.schedule: "@every 1m"
 | 
			
		||||
      ofelia.job-exec.stacker_lnd_channel_cron.command: >
 | 
			
		||||
      ofelia.job-exec.lnd_channel_cron.schedule: "@every 1m"
 | 
			
		||||
      ofelia.job-exec.lnd_channel_cron.command: >
 | 
			
		||||
        su lnd -c bash -c "
 | 
			
		||||
          if [ $$(lncli getinfo | jq '.num_active_channels + .num_pending_channels') -ge 3 ]; then
 | 
			
		||||
            exit 0
 | 
			
		||||
@ -439,11 +445,11 @@ services:
 | 
			
		||||
      <<: *healthcheck
 | 
			
		||||
      test: ["CMD", "curl", "-f", "http://localhost:8443"]
 | 
			
		||||
    depends_on:
 | 
			
		||||
      stacker_lnd:
 | 
			
		||||
      lnd:
 | 
			
		||||
        condition: service_healthy
 | 
			
		||||
        restart: true
 | 
			
		||||
    volumes:
 | 
			
		||||
      - stacker_lnd:/lnd
 | 
			
		||||
      - lnd:/lnd
 | 
			
		||||
    ports:
 | 
			
		||||
      - "8443:8443"
 | 
			
		||||
    command:
 | 
			
		||||
@ -454,7 +460,7 @@ services:
 | 
			
		||||
      - '--lnd-mode=remote'
 | 
			
		||||
      - '--network=regtest'
 | 
			
		||||
      - '--remote.lit-debuglevel=debug'
 | 
			
		||||
      - '--remote.lnd.rpcserver=stacker_lnd:10009'
 | 
			
		||||
      - '--remote.lnd.rpcserver=lnd:10009'
 | 
			
		||||
      - '--remote.lnd.macaroonpath=/lnd/data/chain/bitcoin/regtest/admin.macaroon'
 | 
			
		||||
      - '--remote.lnd.tlscertpath=/lnd/tls.cert'
 | 
			
		||||
      - '--autopilot.disable'
 | 
			
		||||
@ -462,11 +468,13 @@ services:
 | 
			
		||||
      - '--loop.server.host=test.swap.lightning.today:11010'
 | 
			
		||||
    labels:
 | 
			
		||||
      CONNECT: "localhost:8443"
 | 
			
		||||
      CLI: "litcli"
 | 
			
		||||
      CLI_ARGS: "-n regtest --rpcserver localhost:8444"
 | 
			
		||||
    cpu_shares: "${CPU_SHARES_MODERATE}"
 | 
			
		||||
  stacker_cln:
 | 
			
		||||
  cln:
 | 
			
		||||
    build:
 | 
			
		||||
      context: ./docker/cln
 | 
			
		||||
    container_name: stacker_cln
 | 
			
		||||
    container_name: cln
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    profiles:
 | 
			
		||||
      - payments
 | 
			
		||||
@ -482,7 +490,7 @@ services:
 | 
			
		||||
    command:
 | 
			
		||||
      - 'lightningd'
 | 
			
		||||
      - '--network=regtest'
 | 
			
		||||
      - '--alias=stacker_cln'
 | 
			
		||||
      - '--alias=cln'
 | 
			
		||||
      - '--bitcoin-rpcconnect=bitcoin'
 | 
			
		||||
      - '--bitcoin-rpcuser=${RPC_USER}'
 | 
			
		||||
      - '--bitcoin-rpcpassword=${RPC_PASS}'
 | 
			
		||||
@ -495,12 +503,16 @@ services:
 | 
			
		||||
    ports:
 | 
			
		||||
      - "${STACKER_CLN_REST_PORT}:3010"
 | 
			
		||||
    volumes:
 | 
			
		||||
      - stacker_cln:/home/clightning/.lightning
 | 
			
		||||
      - cln:/home/clightning/.lightning
 | 
			
		||||
      - tordata:/home/clightning/.tor
 | 
			
		||||
    labels:
 | 
			
		||||
      TORDIR: "/home/clightning/.tor"
 | 
			
		||||
      CLI: "lightning-cli"
 | 
			
		||||
      CLI_USER: "clightning"
 | 
			
		||||
      CLI_ARGS: "--regtest"
 | 
			
		||||
      ofelia.enabled: "true"
 | 
			
		||||
      ofelia.job-exec.stacker_cln_channel_cron.schedule: "@every 1m"
 | 
			
		||||
      ofelia.job-exec.stacker_cln_channel_cron.command: >
 | 
			
		||||
      ofelia.job-exec.cln_channel_cron.schedule: "@every 1m"
 | 
			
		||||
      ofelia.job-exec.cln_channel_cron.command: >
 | 
			
		||||
        su clightning -c bash -c "
 | 
			
		||||
          if [ $$(lightning-cli --regtest getinfo | jq '.num_active_channels + .num_pending_channels') -ge 3 ]; then
 | 
			
		||||
            exit 0
 | 
			
		||||
@ -519,8 +531,8 @@ services:
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - bitcoin
 | 
			
		||||
      - sn_lnd
 | 
			
		||||
      - stacker_lnd
 | 
			
		||||
      - stacker_cln
 | 
			
		||||
      - lnd
 | 
			
		||||
      - cln
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    command: daemon --docker -f label=com.docker.compose.project=${COMPOSE_PROJECT_NAME}
 | 
			
		||||
    volumes:
 | 
			
		||||
@ -550,12 +562,12 @@ services:
 | 
			
		||||
      - wallets
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    depends_on:
 | 
			
		||||
      stacker_lnd:
 | 
			
		||||
      lnd:
 | 
			
		||||
        condition: service_healthy
 | 
			
		||||
        restart: true
 | 
			
		||||
    volumes:
 | 
			
		||||
      - nwc_send:/app
 | 
			
		||||
      - stacker_lnd:/app/.lnd
 | 
			
		||||
      - lnd:/app/.lnd
 | 
			
		||||
    environment:
 | 
			
		||||
      - RUST_LOG=info
 | 
			
		||||
    entrypoint:
 | 
			
		||||
@ -567,7 +579,7 @@ services:
 | 
			
		||||
      - '--cert-file'
 | 
			
		||||
      - '/app/.lnd/tls.cert'
 | 
			
		||||
      - '--lnd-host'
 | 
			
		||||
      - 'stacker_lnd'
 | 
			
		||||
      - 'lnd'
 | 
			
		||||
      - '--lnd-port'
 | 
			
		||||
      - '10009'
 | 
			
		||||
      - '--max-amount'
 | 
			
		||||
@ -584,12 +596,12 @@ services:
 | 
			
		||||
      - wallets
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    depends_on:
 | 
			
		||||
      stacker_lnd:
 | 
			
		||||
      lnd:
 | 
			
		||||
        condition: service_healthy
 | 
			
		||||
        restart: true
 | 
			
		||||
    volumes:
 | 
			
		||||
      - nwc_recv:/app
 | 
			
		||||
      - stacker_lnd:/app/.lnd
 | 
			
		||||
      - lnd:/app/.lnd
 | 
			
		||||
    environment:
 | 
			
		||||
      - RUST_LOG=info
 | 
			
		||||
    entrypoint:
 | 
			
		||||
@ -601,7 +613,7 @@ services:
 | 
			
		||||
      - '--cert-file'
 | 
			
		||||
      - '/app/.lnd/tls.cert'
 | 
			
		||||
      - '--lnd-host'
 | 
			
		||||
      - 'stacker_lnd'
 | 
			
		||||
      - 'lnd'
 | 
			
		||||
      - '--lnd-port'
 | 
			
		||||
      - '10009'
 | 
			
		||||
      - '--max-amount'
 | 
			
		||||
@ -624,29 +636,30 @@ services:
 | 
			
		||||
      tor:
 | 
			
		||||
        condition: service_healthy
 | 
			
		||||
        restart: true
 | 
			
		||||
      stacker_lnd:
 | 
			
		||||
      lnd:
 | 
			
		||||
        condition: service_healthy
 | 
			
		||||
        restart: true
 | 
			
		||||
    environment:
 | 
			
		||||
      - LNBITS_ADMIN_UI=true
 | 
			
		||||
      - LNBITS_BACKEND_WALLET_CLASS=LndWallet
 | 
			
		||||
      - LND_GRPC_ENDPOINT=stacker_lnd
 | 
			
		||||
      - LND_GRPC_ENDPOINT=lnd
 | 
			
		||||
      - LND_GRPC_PORT=10009
 | 
			
		||||
      - LND_GRPC_CERT=/app/.lnd/tls.cert
 | 
			
		||||
      - LND_GRPC_MACAROON=/app/.lnd/data/chain/bitcoin/regtest/admin.macaroon
 | 
			
		||||
    volumes:
 | 
			
		||||
      - stacker_lnd:/app/.lnd
 | 
			
		||||
      - lnd:/app/.lnd
 | 
			
		||||
      - tordata:/app/.tor
 | 
			
		||||
    labels:
 | 
			
		||||
      CONNECT: "localhost:${LNBITS_WEB_PORT}"
 | 
			
		||||
      TORDIR: "/app/.tor"
 | 
			
		||||
    cpu_shares: "${CPU_SHARES_LOW}"
 | 
			
		||||
volumes:
 | 
			
		||||
  db:
 | 
			
		||||
  os:
 | 
			
		||||
  bitcoin:
 | 
			
		||||
  sn_lnd:
 | 
			
		||||
  stacker_lnd:
 | 
			
		||||
  stacker_cln:
 | 
			
		||||
  lnd:
 | 
			
		||||
  cln:
 | 
			
		||||
  s3:
 | 
			
		||||
  nwc_send:
 | 
			
		||||
  nwc_recv:
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,3 @@
 | 
			
		||||
HiddenServicePort 10009 stacker_lnd:10009
 | 
			
		||||
HiddenServicePort 3010 stacker_cln:3010
 | 
			
		||||
HiddenServicePort 10009 lnd:10009
 | 
			
		||||
HiddenServicePort 3010 cln:3010
 | 
			
		||||
HiddenServicePort 5000 lnbits:5000
 | 
			
		||||
 | 
			
		||||
@ -21,9 +21,9 @@ function mergeServices {
 | 
			
		||||
    cat /services.conf >> /tordata/torrc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# There is a circular dependency between tor and stacker_lnd:
 | 
			
		||||
#  <-> tor needs stacker_lnd to be running to resolve the hidden service target
 | 
			
		||||
#  <-> stacker_lnd needs to wait for tor to start and generate the hidden service address
 | 
			
		||||
# There is a circular dependency between tor and lnd:
 | 
			
		||||
#  <-> tor needs lnd to be running to resolve the hidden service target
 | 
			
		||||
#  <-> lnd needs to wait for tor to start and generate the hidden service address
 | 
			
		||||
# Afaik there isn't an "official" solution for this issue.
 | 
			
		||||
#
 | 
			
		||||
# This workaround starts tor the first time without the lnd hidden service
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										369
									
								
								sndev
									
									
									
									
									
								
							
							
						
						
									
										369
									
								
								sndev
									
									
									
									
									
								
							@ -32,46 +32,6 @@ docker__exec() {
 | 
			
		||||
  DOCKER_CLI_HINTS=false command docker exec -i "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
docker__sn_lnd() {
 | 
			
		||||
  t=$1
 | 
			
		||||
  if [ "$t" = "-t" ]; then
 | 
			
		||||
    shift
 | 
			
		||||
  else
 | 
			
		||||
    t=""
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  docker__exec $t -u lnd sn_lnd lncli "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
docker__stacker_lnd() {
 | 
			
		||||
  t=$1
 | 
			
		||||
  if [ "$t" = "-t" ]; then
 | 
			
		||||
    shift
 | 
			
		||||
  else
 | 
			
		||||
    t=""
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  docker__exec $t -u lnd stacker_lnd lncli "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
docker__stacker_cln() {
 | 
			
		||||
  t=$1
 | 
			
		||||
  
 | 
			
		||||
  if [ "$t" = "-t" ]; then
 | 
			
		||||
    shift
 | 
			
		||||
  else
 | 
			
		||||
    t=""
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$1" = "get_onion" ]; then
 | 
			
		||||
    onion="$(docker__exec $t stacker_cln cat /home/clightning/.tor/hidden_service/hostname | tr -d '[:space:]')"
 | 
			
		||||
    echo "$onion:3010"
 | 
			
		||||
    exit 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  docker__exec $t -u clightning stacker_cln lightning-cli --regtest "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__start() {
 | 
			
		||||
  shift
 | 
			
		||||
 | 
			
		||||
@ -232,38 +192,41 @@ USAGE
 | 
			
		||||
  echo "$help"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__fund_user() {
 | 
			
		||||
sndev__set_balance() {
 | 
			
		||||
  shift
 | 
			
		||||
 | 
			
		||||
  if [ -z "$1" ]; then
 | 
			
		||||
    echo "<nym> argument required"
 | 
			
		||||
    sndev__help_fund_user
 | 
			
		||||
    echo "NYM argument required"
 | 
			
		||||
    sndev__help_set_balance
 | 
			
		||||
    exit 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$2" ]; then
 | 
			
		||||
    echo "<msats> argument required"
 | 
			
		||||
    sndev__help_fund_user
 | 
			
		||||
    echo "MSATS argument required"
 | 
			
		||||
    sndev__help_set_balance
 | 
			
		||||
    exit 2
 | 
			
		||||
  fi
 | 
			
		||||
  re='^[0-9]+$'
 | 
			
		||||
  if ! [[ $2 =~ $re ]]; then
 | 
			
		||||
   echo "<msats> is not a positive integer"
 | 
			
		||||
   sndev__help_fund_user
 | 
			
		||||
 | 
			
		||||
  if ! echo "$2" | grep -qE "^[0-9]+$"; then
 | 
			
		||||
   echo "MSATS argument is not a positive integer"
 | 
			
		||||
   sndev__help_set_balance
 | 
			
		||||
   exit 3
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  docker__exec db psql -U sn -d stackernews -q <<EOF
 | 
			
		||||
    UPDATE users set msats = $2 where name = '$1';
 | 
			
		||||
EOF
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_fund_user() {
 | 
			
		||||
sndev__help_set_balance() {
 | 
			
		||||
  help="
 | 
			
		||||
fund a nym without using an LN invoice (local only)
 | 
			
		||||
set the balance of a nym
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev fund_user <nym> <msats>
 | 
			
		||||
  $ sndev set_balance NYM MSATS
 | 
			
		||||
 | 
			
		||||
  <nym> - the name of the user you want to fund
 | 
			
		||||
  <msats> - the amount of millisatoshis to set the account to. Must be >= 0
 | 
			
		||||
  NYM - the name of the user you want to set the balance of
 | 
			
		||||
  MSATS - the amount of millisatoshis to set the account to. Must be >= 0
 | 
			
		||||
"
 | 
			
		||||
 | 
			
		||||
  echo "$help"
 | 
			
		||||
@ -271,7 +234,12 @@ USAGE
 | 
			
		||||
 | 
			
		||||
sndev__fund() {
 | 
			
		||||
  shift
 | 
			
		||||
  docker__stacker_lnd -t payinvoice "$@"
 | 
			
		||||
  if [ "$1" = "--cln" ]; then
 | 
			
		||||
    shift
 | 
			
		||||
    sndev__cli -t cln pay "$@"
 | 
			
		||||
  else
 | 
			
		||||
    sndev__cli -t lnd payinvoice "$@"
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_fund() {
 | 
			
		||||
@ -279,46 +247,24 @@ help="
 | 
			
		||||
pay a bolt11 for funding
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev fund <bolt11> [OPTIONS]
 | 
			
		||||
  $ sndev fund BOLT11 [OPTIONS]
 | 
			
		||||
  $ sndev fund --cln BOLT11
 | 
			
		||||
 | 
			
		||||
OPTIONS"
 | 
			
		||||
 | 
			
		||||
  echo "$help"
 | 
			
		||||
  docker__stacker_lnd payinvoice -h | awk '/OPTIONS:/{y=1;next}y' | awk '!/^[\t ]+--pay_req value/'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__cln_fund() {
 | 
			
		||||
  shift
 | 
			
		||||
  docker__stacker_cln -t pay "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_cln_fund() {
 | 
			
		||||
help="
 | 
			
		||||
pay a bolt11 for funding with CLN
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev cln_fund <bolt11>"
 | 
			
		||||
  echo "$help"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__cln_withdraw() {
 | 
			
		||||
  shift
 | 
			
		||||
  label=$(date +%s)
 | 
			
		||||
  docker__stacker_cln -t invoice "$1" "$label" sndev | jq -r '.bolt11'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_cln_withdraw() {
 | 
			
		||||
help="
 | 
			
		||||
create a bolt11 for withdrawal with CLN
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev cln_withdraw <amount msats>"
 | 
			
		||||
  echo "$help"
 | 
			
		||||
  sndev__cli lnd payinvoice -h | awk '/OPTIONS:/{y=1;next}y' | awk '!/^[\t ]+--pay_req value/'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__withdraw() {
 | 
			
		||||
  shift
 | 
			
		||||
  docker__stacker_lnd addinvoice --amt "$@" | jq -r '.payment_request'
 | 
			
		||||
  if [ "$1" = "--cln" ]; then
 | 
			
		||||
    shift
 | 
			
		||||
    label=$(date +%s)
 | 
			
		||||
    sndev__cli -t cln invoice "$1" "$label" sndev | jq -j '.bolt11'; echo
 | 
			
		||||
  else
 | 
			
		||||
    sndev__cli lnd addinvoice --amt "$@" | jq -j '.payment_request'; echo
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_withdraw() {
 | 
			
		||||
@ -326,12 +272,13 @@ sndev__help_withdraw() {
 | 
			
		||||
create a bolt11 for withdrawal
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev withdraw <amount sats> [OPTIONS]
 | 
			
		||||
  $ sndev withdraw SATS [OPTIONS]
 | 
			
		||||
  $ sndev withdraw --cln SATS
 | 
			
		||||
 | 
			
		||||
OPTIONS"
 | 
			
		||||
 | 
			
		||||
  echo "$help"
 | 
			
		||||
  docker__stacker_lnd addinvoice -h | awk '/OPTIONS:/{y=1;next}y' | awk '!/^[\t ]+(--amt|--amt_msat) value/'
 | 
			
		||||
  sndev__cli lnd addinvoice -h | awk '/OPTIONS:/{y=1;next}y' | awk '!/^[\t ]+(--amt|--amt_msat) value/'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__psql() {
 | 
			
		||||
@ -387,6 +334,30 @@ USAGE
 | 
			
		||||
  echo "$help"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__test() {
 | 
			
		||||
  shift
 | 
			
		||||
 | 
			
		||||
  args=""
 | 
			
		||||
  if [ $# -gt 0 ]; then
 | 
			
		||||
    args="-- $@"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  docker__exec -t -u apprunner app npm run test $args
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_test() {
 | 
			
		||||
  help="
 | 
			
		||||
run tests
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev test [OPTIONS]
 | 
			
		||||
 | 
			
		||||
OPTIONS"
 | 
			
		||||
 | 
			
		||||
  echo "$help"
 | 
			
		||||
  docker__exec -u apprunner app npm run test -- --help | awk '/Options:/{y=1;next}y'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__compose() {
 | 
			
		||||
  shift
 | 
			
		||||
  docker__compose "$@"
 | 
			
		||||
@ -396,45 +367,6 @@ sndev__help_compose() {
 | 
			
		||||
  docker__compose --help
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__sn_lndcli() {
 | 
			
		||||
  shift
 | 
			
		||||
  docker__sn_lnd -t "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_sn_lndcli() {
 | 
			
		||||
  docker__sn_lnd --help
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__stacker_lndcli() {
 | 
			
		||||
  shift
 | 
			
		||||
  docker__stacker_lnd -t "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_stacker_lndcli() {
 | 
			
		||||
  docker__stacker_lnd --help
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__stacker_clncli() {
 | 
			
		||||
  shift
 | 
			
		||||
  docker__stacker_cln -t "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_stacker_clncli() {
 | 
			
		||||
  docker__stacker_cln help
 | 
			
		||||
  echo
 | 
			
		||||
  echo "EXTRA:"
 | 
			
		||||
  echo "  get_onion          get the onion address"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__stacker_litcli() {
 | 
			
		||||
  shift
 | 
			
		||||
  docker__exec -t litd litcli -n regtest --rpcserver localhost:8444 "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_stacker_litcli() {
 | 
			
		||||
  docker__exec -t litd litcli -h
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__sndev__pr_track() {
 | 
			
		||||
  json=$(curl -fsSH "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/stackernews/stacker.news/pulls/$1")
 | 
			
		||||
  case $(git config --get remote.origin.url) in
 | 
			
		||||
@ -484,7 +416,7 @@ sndev__help_pr() {
 | 
			
		||||
fetch and checkout a pr
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev pr [OPTIONS] <pr number>
 | 
			
		||||
  $ sndev pr [OPTIONS] PR_NUMBER
 | 
			
		||||
 | 
			
		||||
OPTIONS
 | 
			
		||||
  -t, --track   track the pr in a new branch, creating a remote if necessary
 | 
			
		||||
@ -497,7 +429,7 @@ OPTIONS
 | 
			
		||||
sndev__login() {
 | 
			
		||||
  shift
 | 
			
		||||
  if [ -z "$1" ]; then
 | 
			
		||||
    echo "<nym> argument required"
 | 
			
		||||
    echo "NYM argument required"
 | 
			
		||||
    sndev__help_login
 | 
			
		||||
    exit 1
 | 
			
		||||
  fi
 | 
			
		||||
@ -528,84 +460,102 @@ sndev__help_login() {
 | 
			
		||||
login as a nym
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev login <nym>
 | 
			
		||||
  $ sndev login NYM
 | 
			
		||||
"
 | 
			
		||||
 | 
			
		||||
  echo "$help"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_stacker_lnd() {
 | 
			
		||||
  help="
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev stacker_lnd get_cert         get the tls cert
 | 
			
		||||
  $ sndev stacker_lnd get_onion        get the onion address
 | 
			
		||||
"
 | 
			
		||||
 | 
			
		||||
  echo "$help"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__stacker_lnd() {  
 | 
			
		||||
sndev__onion() {
 | 
			
		||||
  shift
 | 
			
		||||
  if [ -z "$1" ]; then
 | 
			
		||||
    echo "no command provided"
 | 
			
		||||
    sndev__help_stacker_lnd
 | 
			
		||||
  tordir=$(docker__compose ps $1 --format '{{.Label "TORDIR"}}')
 | 
			
		||||
  if [ -z "$tordir" ]; then
 | 
			
		||||
    echo "no TORDIR label found for $1"
 | 
			
		||||
    exit 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ "$1" = "get_cert" ]; then
 | 
			
		||||
    echo $(docker__exec -t stacker_lnd openssl base64 -A -in /home/lnd/.lnd/tls.cert)
 | 
			
		||||
  elif [ "$1" = "get_onion" ];
 | 
			
		||||
  then
 | 
			
		||||
    onion="$(docker__exec -t stacker_lnd cat /home/lnd/.tor/hidden_service/hostname | tr -d '[:space:]')"
 | 
			
		||||
    echo "$onion:10009"
 | 
			
		||||
  fi  
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_lnbits() {
 | 
			
		||||
  help="
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev stacker_lnbits get_onion        get the onion address
 | 
			
		||||
"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__lnbits() {  
 | 
			
		||||
  shift
 | 
			
		||||
  if [ -z "$1" ]; then
 | 
			
		||||
    echo "no command provided"
 | 
			
		||||
    sndev__help_lnbits
 | 
			
		||||
    exit 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ "$1" = "get_onion" ];
 | 
			
		||||
  then
 | 
			
		||||
    onion="$(docker__exec -t lnbits cat /app/.tor/hidden_service/hostname | tr -d '[:space:]')"
 | 
			
		||||
    echo "$onion:5000"
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
sndev__help_tor() {
 | 
			
		||||
  help="
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev tor get_onion         get the onion address
 | 
			
		||||
"
 | 
			
		||||
  
 | 
			
		||||
  echo "$help"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__tor() {  
 | 
			
		||||
  shift
 | 
			
		||||
  if [ -z "$1" ]; then
 | 
			
		||||
    echo "no command provided"
 | 
			
		||||
    sndev__help_tor
 | 
			
		||||
    exit 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ "$1" = "get_onion" ];
 | 
			
		||||
  then
 | 
			
		||||
    onion="$(docker__exec -t stacker_lnd cat /home/lnd/.tor/hidden_service/hostname | tr -d '[:space:]')"
 | 
			
		||||
  onion=$(docker__exec $1 cat $tordir/hidden_service/hostname | tr -d '[:space:]')
 | 
			
		||||
  echo "$onion"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_onion() {
 | 
			
		||||
  help="
 | 
			
		||||
get the onion address of a service
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev onion SERVICE
 | 
			
		||||
"
 | 
			
		||||
 | 
			
		||||
  echo "$help"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__cert() {
 | 
			
		||||
  shift
 | 
			
		||||
  certdir=$(docker__compose ps $1 --format '{{.Label "CERTDIR"}}')
 | 
			
		||||
  if [ -z "$certdir" ]; then
 | 
			
		||||
    echo "no CERTDIR label found for $1"
 | 
			
		||||
    exit 1
 | 
			
		||||
  fi
 | 
			
		||||
  docker__exec $1 cat $certdir/tls.cert | base64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_cert() {
 | 
			
		||||
  help="
 | 
			
		||||
get the tls cert of a service
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev cert SERVICE
 | 
			
		||||
"
 | 
			
		||||
 | 
			
		||||
  echo "$help"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__cli() {
 | 
			
		||||
  t=$1
 | 
			
		||||
 | 
			
		||||
  if [ "$t" = "-t" ]; then
 | 
			
		||||
    shift
 | 
			
		||||
  else
 | 
			
		||||
    t=""
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$1" = "cli" ]; then
 | 
			
		||||
    shift
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$1" ]; then
 | 
			
		||||
    echo "SERVICE required"
 | 
			
		||||
    sndev__help_cli
 | 
			
		||||
    exit 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  service=$1
 | 
			
		||||
  cli=$(docker__compose ps $service --format '{{.Label "CLI"}}')
 | 
			
		||||
  cli_user=$(docker__compose ps $service --format '{{.Label "CLI_USER"}}')
 | 
			
		||||
  cli_args=$(docker__compose ps $service --format '{{.Label "CLI_ARGS"}}')
 | 
			
		||||
 | 
			
		||||
  if [ -z "$cli" ]; then
 | 
			
		||||
    echo "no CLI label found for $service"
 | 
			
		||||
    exit 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  shift
 | 
			
		||||
 | 
			
		||||
  if [ -n "$cli_user" ]; then
 | 
			
		||||
    cli_user="-u $cli_user"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  docker__exec $t $cli_user $service $cli $cli_args "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help_cli() {
 | 
			
		||||
  help="
 | 
			
		||||
run a cli command on a service
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
  $ sndev cli SERVICE [COMMAND [ARGS]]
 | 
			
		||||
"
 | 
			
		||||
 | 
			
		||||
  echo "$help"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sndev__help() {
 | 
			
		||||
@ -643,16 +593,12 @@ COMMANDS
 | 
			
		||||
 | 
			
		||||
  sn:
 | 
			
		||||
    login                 login as a nym
 | 
			
		||||
    fund_user             fund a nym without using an LN invoice
 | 
			
		||||
    set_balance           set the balance of a nym
 | 
			
		||||
 | 
			
		||||
  lnd:
 | 
			
		||||
  lightning:
 | 
			
		||||
    fund                   pay a bolt11 for funding
 | 
			
		||||
    withdraw               create a bolt11 for withdrawal
 | 
			
		||||
 | 
			
		||||
  cln:
 | 
			
		||||
    cln_fund               pay a bolt11 for funding with CLN
 | 
			
		||||
    cln_withdraw           create a bolt11 for withdrawal with CLN
 | 
			
		||||
 | 
			
		||||
  db:
 | 
			
		||||
    psql                   open psql on db
 | 
			
		||||
    prisma                 run prisma commands
 | 
			
		||||
@ -660,19 +606,14 @@ COMMANDS
 | 
			
		||||
  dev:
 | 
			
		||||
    pr                     fetch and checkout a pr
 | 
			
		||||
    lint                   run linters
 | 
			
		||||
    open                   open container url in browser
 | 
			
		||||
    test                   run tests
 | 
			
		||||
 | 
			
		||||
  other:
 | 
			
		||||
    cli                    service cli passthrough
 | 
			
		||||
    open                   open service GUI in browser
 | 
			
		||||
    onion                  service onion address
 | 
			
		||||
    cert                   service tls cert
 | 
			
		||||
    compose                docker compose passthrough
 | 
			
		||||
    sn_lndcli              lncli passthrough on sn_lnd
 | 
			
		||||
    stacker_lndcli         lncli passthrough on stacker_lnd
 | 
			
		||||
    stacker_clncli         lightning-cli passthrough on stacker_cln
 | 
			
		||||
    stacker_litcli         litcli passthrough on litd
 | 
			
		||||
    tor get_onion          get the onion address
 | 
			
		||||
    stacker_lnd get_cert   get the tls cert
 | 
			
		||||
    stacker_lnd get_onion  get the onion address
 | 
			
		||||
    stacker_clncli get_onion  get the onion address
 | 
			
		||||
    lndbits get_onion      get the onion address
 | 
			
		||||
"
 | 
			
		||||
  echo "$help"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,20 +2,20 @@ For testing cln as an attached receiving wallet, you'll need a rune and the cert
 | 
			
		||||
 | 
			
		||||
# host and port
 | 
			
		||||
 | 
			
		||||
`stacker_cln:3010`
 | 
			
		||||
`cln:3010`
 | 
			
		||||
 | 
			
		||||
# host and port (onion)
 | 
			
		||||
 | 
			
		||||
Run:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
sndev stacker_clncli get_onion
 | 
			
		||||
sndev onion cln
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# create rune
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
sndev stacker_clncli --regtest createrune restrictions='["method=invoice"]'
 | 
			
		||||
sndev cli cln --regtest createrune restrictions='["method=invoice"]'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# get cert
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ Or simply copy the keys from here:
 | 
			
		||||
 | 
			
		||||
( These keys can be found under `Node URL, API keys and API docs`. )
 | 
			
		||||
 | 
			
		||||
To use the same URL to connect to LNbits in the browser and server during local development, `localhost:<port>` is mapped to `lnbits:5000` on the server.
 | 
			
		||||
To use the same URL to connect to LNbits in the browser and server during local development, `http://localhost:<port>` is mapped to `http://lnbits:5000` on the server.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# tor onion url
 | 
			
		||||
@ -29,5 +29,5 @@ To use the same URL to connect to LNbits in the browser and server during local
 | 
			
		||||
Run the following command to get the onion url:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
sndev lnbits get_onion
 | 
			
		||||
sndev onion lnbits
 | 
			
		||||
```
 | 
			
		||||
@ -2,20 +2,22 @@ For testing lnd as an attached receiving wallet, you'll need a macaroon and the
 | 
			
		||||
 | 
			
		||||
# host and port
 | 
			
		||||
 | 
			
		||||
`stacker_lnd:10009`
 | 
			
		||||
`lnd:10009`
 | 
			
		||||
 | 
			
		||||
## host and port (onion)
 | 
			
		||||
 | 
			
		||||
To get the onion address run this command:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
sndev stacker_lnd get_onion
 | 
			
		||||
sndev onion lnd
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then use port 10009 on the onion address.
 | 
			
		||||
 | 
			
		||||
# generate macaroon
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
sndev stacker_lndcli -n regtest bakemacaroon invoices:write invoices:read
 | 
			
		||||
sndev cli lnd -n regtest bakemacaroon invoices:write invoices:read
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# get cert
 | 
			
		||||
@ -23,5 +25,5 @@ sndev stacker_lndcli -n regtest bakemacaroon invoices:write invoices:read
 | 
			
		||||
To get the cert run this command:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
sndev stacker_lnd get_cert
 | 
			
		||||
sndev cert lnd
 | 
			
		||||
```
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
Using webln will require installing the alby browser extension and connecting it to an alby hub connected to `stacker_lnd`.
 | 
			
		||||
Using webln will require installing the alby browser extension and connecting it to an alby hub connected to `lnd`.
 | 
			
		||||
 | 
			
		||||
1. Install the [Alby browser extensions](https://chromewebstore.google.com/detail/alby-bitcoin-wallet-for-l/iokeahhehimjnekafflcihljlcjccdbe?pli=1)
 | 
			
		||||
2. Create an Alby account
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user