Linux things 🐧

un blog sur les technologies des logiciels libres et autres digressions

Bitcoin: un noeud Lightning Network

Wed, 10 Apr 2024 19:30:23 +0200
# nomad   # docker   # lnd   # lnaddrd   # lightning  

addendum

[mise Ă  jour 23.04.24]

  • correction de sĂ©curitĂ©
  • ajout de tailscale
  • ajout de lnaddrd

AprĂšs un Ă©change avec le dĂ©veloppeur de lnaddrd, il s’avĂšre qu’utiliser l’API REST de lnd sans son certificat gĂ©nĂ©rĂ© m’expose Ă  des attaques Man in the Middle. L’article est donc corrigĂ© avec la configuration adaptĂ©.

Introduction

Dans mon prĂ©cĂ©dent article sur Nostr j’avais rapidement parlĂ© de Bitcoin et du lightning network. Pour rappel le lightning network est une surcouche (layer 2) Ă  la blockchain Bitcoin, il a Ă©tĂ© conçu pour ĂȘtre trĂšs rapide en fonctionnant off chain. Il existe un grand nombre de littĂ©ratures Ă  ce sujet, en commencant par lightning.fr, je vous laisse faire votre recherche.

Ici je ne vais pas expliquer les arcanes de cette techno, Ă©tant moi mĂȘme dĂ©butant, mais simplement montrer comment dĂ©ployer et hĂ©berger un noeud lnd sur une infra Nomad. Il existe plusieurs implĂ©mentations de la spec, comme Eclair en scala (jvm) de la sociĂ©tĂ© ACINQ ou bien rust-lightning. J’ai choisi lnd car il est en Go et bien intĂ©grĂ© Ă  Zeus un wallet non custodial. Alors on entre ici dans un nouveau monde, celui de la crypto monnaie, qui possĂšde 3 tonnes de termes Ă©tranges et de concepts abscons. Il existe de nombreuses chaines Youtube et Twitch dĂ©diĂ©es comme celles de Hasheur qui expliquent tout mais quand vous dĂ©barquez c’est incomprĂ©hensible đŸ„Ž
En rĂ©sumĂ© non custodial veut dire que vous possĂ©dez physiquement vos cryptos, mais charge Ă  vous de les sĂ©curiser et les sauvegarder. Custodial veut dire que vous passez par un tiers Ă  qui vous dĂ©lĂ©guez le stockage et la sĂ©curisation de vos cryptos, comme une banque donc ; c’est plus simple mais vous ne possedez pas votre clĂ© privĂ©e. Les anciens clients de Mt.Gox/QuadrigaCX/FTX/NextFuckingScammer comprendront ce que je veux dire đŸ€§

Zeus

Dans l’article sur Nostr je parlais de Alby qui permet de simplifier la connexion Ă  Nostr, mais propose aussi de gĂ©rer vos Satoshis via une adresse @getalby.com. C’est donc un portefeuille custodial, ce qui n’est pas forcĂ©ment gĂ©nant pour de petites sommes mais l’objectif ici est d’une part d’ĂȘtre complĂȘtement indĂ©pendant et d’autre part participer au dĂ©veloppement du rĂ©seau lightning.
J’ai dĂ©butĂ© avec Zeus en connectant l’application avec mon compte Alby comme expliquĂ© ici. Au final, j’ai reçu quelques Satoshi via Nostr đŸ€™ en prĂ©cisant mon adresse fredix@getalby.com dans le client Nostr. Puis je pouvais les consulter sur l’application mobile Zeus et en thĂ©orie effectuer des paiements via cette mĂȘme application dans les commerces acceptant le lightning (je n’ai pas encore eu l’occasion de tester).
Zeus permet d’ajouter plusieurs comptes, comme un noeud embarquĂ© dans l’application, ou un noeud externe LND REST ce que l’on va utiliser.

LND

Pour tout dire, ce qui m’a motivĂ© Ă  me lancer est ce post sur Stacker.news incredibly impressed by LND + Neutrino.

Et surtout

I’ve run Lightning nodes before, but they were always backed by a full archival Bitcoin Core instance, with hundreds of gigabytes of storage required for the full chain and index. This time, I wanted to have a go at running an ultra-light lnd instance on my puny little VPS with as tiny a storage footprint as possible.

Et oui, de base un noeud lightning a besoin de s’interfacer avec un noeud Bitcoin pour Ă©crire dans la blockchain quand c’est nĂ©cessaire. Et qui dit noeud Bitcoin dit des centaines de giga de donnĂ©es blockchain (Ă  ce jour environ 563.61 Go d’aprĂšs ce site). Or neutrino permet de s’en passer et de n’utiliser que : 322 Mo sur mon instance 😼

⚠ Attention Ă  ce jour je n’ai aucune idĂ©e du comment tout cela fonctionne prĂ©cisĂ©ment, de plus la configuration en dessous est faite sur le mainnet, le rĂ©seau de prod de Bitcoin. Amusez vous avec de trĂšs petites sommes

Pour tester, utilisez les options --bitcoin.testnet --neutrino.connect=faucet.lightning.community ⚠

Le projet propose des documentations pour dĂ©marrer avec Docker ce qui m’a permis d’Ă©crire la version en HCL. GrĂące au script start-lnd.sh et au fichier de configuration d’exemple sample-lnd.conf j’ai pu dĂ©duire les informations nĂ©cessaires Ă  cette migration.
De base le service utilise 2 ports externes, le 9735 pour se connecter au rĂ©seau P2P lightning et le 8080 (j’utilise le 8081 mais peu importe) pour l’API REST. C’est via cette API que l’on va pouvoir connecter le client mobile Zeus et elle m’a posĂ© quelques problĂšmes. En effet par sĂ©curitĂ© lnd gĂ©nĂšre des certificats tls pour accĂ©der Ă  l’API, certificat Ă  ajouter dans son smartphone Android pour que Zeus puisse se connecter, donc c’est super lourdingue.
Or j’utilise dĂ©jĂ  des certificats Let’s Encrypt gĂ©nĂ©rĂ©s par Caddy qui renvoie vers l’API, la connexion est donc sĂ©curisĂ©e. Caddy doit donc nĂ©cessairement se connecter en HTTP sur le port dans le conteneur. Tout va bien, lnd propose de dĂ©sactiver le TLS avec l’option --no-rest-tls , sauf que si on lui demande d’Ă©couter sur toutes les interfaces rĂ©seau avec --restlisten=0.0.0.0:8081 il refuse de se lancer (mĂȘme soucis que chpio)

detected RPC server listening on publicly reachable interface 0.0.0.0:8081 with encryption disabled! Refusing to start with –no-rest-tls specified

Par sĂ©curitĂ© il n’est pas souhaitable que l’API REST soit accessible depuis Internet. De plus je suis le seul utilisateur de cette API, je vais donc configurer lnd pour que le port REST Ă©coute uniquement sur l’interface rĂ©seau de tailscale.

Je lance le conteneur Docker en utilisant un template Nomad afin d’injecter l’IP du conteneur (mĂȘme si dans l’absolue 0.0.0.0 aurait suffit)

template {
    data = <<EOH
    #!/bin/bash
    CONTAINER_IP=$(ip route get 1.2.3.4 | awk '{print $7}')
	lnd --bitcoin.mainnet --bitcoin.active --bitcoin.node=neutrino 
	--neutrino.addpeer=btcd0.lightning.engineering 
	--feeurl=https://nodes.lightning.computer/fees/v1/btc-fee-estimates.json 
	--restlisten=${CONTAINER_IP}:8081 --tlsextraip=IP_TAILSCALE_NODE1 
	--tlsextradomain=lnd --externalip=VOTRE_PUBLIC_IP --alias=VOTREALIAS	
	EOH
	destination = "local/file.sh"
}      

(Attention les options tlsextraip et tlsextradomain sont trĂšs importantes car c’est ce qui va permettre Ă  votre certificat d’ĂȘtre valide dans Zeus et dans lnaddrd).
Pour forcer le lancement de ce script au dĂ©marrage du conteneur il faut Ă©craser l’entrypoint de l’image, qui lance normalement la commande lnd, par la commande bash:

entrypoint = [
	"bash",
	"-c",
	"chmod 777 /local/file.sh && ./local/file.sh",
]

Voici le fichier HCL final

lnd.hcl

job "lnd" {
  datacenters = ["dc1"]
  type = "service" 
  group "app" {
     count = 1

     network {
				port "tcp" {
				  to     = 9735 # container port the app runs on
				  static = 9735   # host port to expose
				}
				port "rest" {
					to     = 8081 # container port the app runs on
					static = 8081   # host port to expose
					host_network = "tailscale"
			  }		  				

				dns {
				  servers = ["172.17.0.1", "8.8.8.8", "8.8.4.4"]
				}			
      }


     task "lnd" {
     		driver = "docker"

				constraint {
				 attribute = "${attr.unique.hostname}"
				 value     = "node1"
				}

				template {
				    data = <<EOH
				    	#!/bin/bash
				      CONTAINER_IP=$(ip route get 1.2.3.4 | awk '{print $7}')
					  lnd --bitcoin.mainnet --bitcoin.active --bitcoin.node=neutrino --neutrino.addpeer=btcd0.lightning.engineering --feeurl=https://nodes.lightning.computer/fees/v1/btc-fee-estimates.json --restlisten=${CONTAINER_IP}:8081 --tlsextraip=IP_TAILSCALE_NODE1 --tlsextradomain=lnd --externalip=EXTERNAL_PUBLIC_IP --alias=VOTREALIAS
					EOH
				    destination = "local/file.sh"
				}      

				config {
					image = "lightninglabs/lnd:v0.17.4-beta"
				  entrypoint = [
				  	"bash",
				  	"-c",
				  	"chmod 777 /local/file.sh && ./local/file.sh",
				  ]

					volumes = [
						"/votre_volume/lnd:/root/.lnd"
					]				
					 ports = [
					    "tcp", "rest"
					 ]
					}

					resources {
					 cpu = 500
					 memory = 600
					}

					service {
						 name = "lnd"
						 tags = ["global", "app"]
						 provider = "nomad"
						 port = "tcp"
					}
	
					service {
						 name = "lnd"
						 tags = ["global", "app"]
						 provider = "nomad"
						 port = "rest"
					}						

     	}

  }
}

Il faut bien sur changer la section volume avec votre répertoire de destination. Puis:

nomad job run lnd.hcl

lncli

Le conteneur est normalement lancé, maintenant il faut créer son wallet pour pouvoir recevoir et envoyer de la crypto. Pour cela il faut se connecter dans le conteneur sur le host:

docker exec -it ID_DU_CONTENEUR bash

et lancer le commande de création du wallet

lncli create

A ce moment là il faut donner un mot de passe et une passe phrase que je vous conseille de sauvegarder dans un gestionnaire de mots de passe. Bref suivez les consignes demandées par lncli.

Enfin il faut unlock le wallet avec votre mot de passe:

lncli unlock

au bout de quelques minutes vérifiez que votre noeud est bien relié au lightning network:

lncli getinfo

vous devriez voir:

"synced_to_chain": true,
"synced_to_graph": true,

Sortir du conteneur, et derniĂšre action avec le macaroon. C’est un espĂšce de cookie/token qui autorise la connexion Ă  l’API REST. Il faut l’afficher en hexa et le fournir Ă  Zeus:

Host node1

xxd -p -c2000 /votre_volume/lnd/data/chain/bitcoin/mainnet/admin.macaroon

Copier la sortie

Tailscale

Maintenant que le serveur lnd expose son API REST sur l’interface rĂ©seau de tailscale il faut installer le client tailscale sur le smartphone. Une fois tout configurĂ© et le VPN activĂ© on devrait voir comme ici le serveur Node1.

tailscale

Zeus

Retour Ă  Zeus pour terminer. Il est nĂ©cessaire d’envoyer sur le smartphone le certificat tls gĂ©nĂ©rĂ© par lnd. Il se trouve sur votre serveur (Node1) dans /votre_volume/lnd/tls.cert ; le copier en tls.crt puis l’envoyer avec par exemple rymdport et recevoir via Wormhole William. Ensuite aller dans les paramĂštres Android, SĂ©curitĂ© et confidentialitĂ© / (tout en bas) SĂ©curitĂ© et confidentialitĂ© renforcĂ©es / Chiffrement et identifiants / Installer un certificat / Certificat CA / Installer quand mĂȘme (valider) / SĂ©lectionner le fichier tls.crt.

Dans Zeus ajoutez un noeud LND REST et fournir vos informations, dont l’URL de votre domaine gĂ©rĂ© par Caddy, btc.DOMAIN.tld, l’IP Tailscale de Node1, l’hexa du macaroon et le port 8081. Sauvegardez et revenez Ă  l’accueil, il devrait y avoir 2 barres horizontales, Lightning et On-chain, en cliquant sur ce dernier vous pourrez obtenir une adresse BTC avec laquelle vous pourrez recevoir votre premier versement en Ă©tant trĂšs patient.

Concernant lightning je n’ai pas pu envoyer de Satoshis de mon compte Alby vers mon noeud, il y a une notion de canal Ă  ouvrir, mais c’est en cours d’exploration đŸ€”

zeus

lnaddrd

Alby c’est bien mais il serait top d’avoir une adresse@sondomain.tld qui renvoie vers son noeud lnd.
Or j’ai dĂ©couvert il y a peu ce projet de conduition, lnaddrd qui permet d’avoir sa propre adresse lightning et gĂ©nĂ©rer des factures en se connectant Ă  l’instance lnd. Voici le fichier Nomad hcl. A savoir qu’il doit ĂȘtre instanciĂ© sur le rĂ©seau tailscale afin de pouvoir contacter l’API lnd. Et qu’il a besoin d’accĂ©der en lecture seule au volume de lnd pour lire le macaroon invoices et le certificat lnd.

ln.hcl

job "ln" {
  datacenters = ["dc1"]
  type = "service" 
  group "app" {
     count = 1

     network {
				port "http" {
				  to     = 3441 # container port the app runs on
				  host_network = "tailscale"				  
				}
      }

     task "ln" {
     		driver = "docker"

				constraint {
				 attribute = "${attr.unique.hostname}"
				 value     = "node1"
				}

				config {
					image = "fredix/lnaddrd"

					labels = {
						"caddy" = "ln.VOTREDOMAIN.TLD"
	        			"caddy.reverse_proxy" = "{{upstreams 3441}}"
						# remove the following line when you have verified your setup
						# Otherwise you risk being rate limited by let's encrypt
						"caddy.tls.ca" = "https://acme-v02.api.letsencrypt.org/directory"
					}

					extra_hosts = ["lnd:IP_TAILSCALE_NODE1"]

	        mounts = [
		          {
	                type = "bind"
	                target = "/usr/share/lnaddrd"
	                source = "/votre_volume/lnaddrd"
	                readonly = true
	                bind_options = {
	                  propagation = "rshared"
	                }
	            },
						 {
	                type = "bind"
	                target = "/usr/share/lnd"
	                source = "/votre_volume/lnd"
	                readonly = true
	                bind_options = {
	                  propagation = "rshared"
	                }
	            }	            
            ]

					 ports = [
					    "http"
					 ]
					}

					resources {
					 cpu = 300
					 memory = 64
					}

					service {
						 name = "ln"
						 tags = ["global", "app"]
						 provider = "nomad"
						 port = "http"

						 check {
						    type = "http"
						    name = "app_health"
						    path = "/.well-known/lnurlp/VOUS"
						    interval = "20s"
						    timeout = "10s"
						}
					}	

     	}

  }
}

(ce hcl utilise mon image docker “fredix/lnaddrd” que vous pouvez utiliser, il n’y a rien de spĂ©cifique, ou bien gĂ©nĂ©rer la votre, PR en attente).
Ce fichier hcl a besoin d’un fichier lnaddrd.yaml Ă  dĂ©poser dans /votre_volume/lnaddrd ainsi qu’une image png de votre profil.

# This configures how the webserver will bind and expose its HTTP stack.
# By default it serves unencrypted HTTP. Specify a TLS cert+key to serve
# clients over HTTPS instead.
webserver:
  bind_address: 0.0.0.0:3441              # required
  # tls_cert_file: /path/to/server.tls.cert # optional
  # tls_key_file: /path/to/server.tls.key   # optional

lnurl:
  # This must be the base URL of your server.
  url_authority: https://ln.VOTREDOMAIN.tld # required

  # Both of these will be included in the pay request metadata array.
  # The icon_file can be either a PNG or a JPEG file.
  short_description: "Donation to VOUS" # optional
  icon_file: /usr/share/lnaddrd/icon.png                # required
  
  # Determines the range of acceptable payment amounts.
  max_pay_request_sats: 5000000000 # required
  min_pay_request_sats: 100       # required

  # Determines the expiry time of BOLT11 invoices we create.
  # Defaults to whatever the remote LND instance uses by default.
  invoice_expiry: "1h" # optional
  # invoice_expiry: "20m"
  # invoice_expiry: "100s"

# Accept lightning address requests for the following usernames.
lightning_address_usernames: # optional
  - VOUS

# Configure a connection to LND's REST API.
#
# You can find invoices.macaroon in:     ~/.lnd/data/chain/bitcoin/mainnet/invoices.macaroon
# You can find LND's TLS certificate in: ~/.lnd/tls.cert
#
# Note that your LND certificate MUST have the 'host' field listed as a SAN.
# (hint: use the 'tlsextradomain' option in lnd.conf)
lnd:
  host: lnd:8081                  # required
  macaroon_file: /usr/share/lnd/data/chain/bitcoin/mainnet/invoices.macaroon # required
  tls_cert_file: /usr/share/lnd/tls.cert      # required
nomad job run ln.hcl

Si vous allez sur l’URL de votre instance : https://ln.VOTREDOMAIN.tld/.well-known/lnurlp/VOUS vous devriez voir un gros json qui contient votre png en base64, exemple https://ln.fredix.xyz/.well-known/lnurlp/fredix

Les banques ?

Comment se positionnent certaines banques ? A priori qui dit banque dit pas copain avec un systĂšme dĂ©centralisĂ©. Cependant hormis les rĂ©actionnaires impossible de faire l’impasse sur l’ocĂ©an de $ que le bitcoin gĂ©nĂšre. Je ne vais pas faire un tour des nĂ©o-banques mais juste en citer 3 qui font plus ou moins de la crypto.

  • N26 s’est lancĂ© depuis peu. Mais c’est juste pour faire du traiding, achat-revente dans leur plateforme. Il n’est pas possible de recevoir de la crypto et d’en envoyer ce qui limite pas mal l’usage.
  • Revolut alors lĂ  c’est la fĂȘte du slip. On peut acheter-vendre mais aussi recevoir et envoyer. J’ai achetĂ© un petit montant de BTC, et j’ai pu en envoyer dans mon wallet Zeus, sur mon noeud embedded mais aussi sur mon noeud LND REST 👍
  • deblock un petit dernier qui vient de sortir. Par des anciens de Revolut, en France et se disent non-custodial. Prometteur sur le papier, il y a une liste d’attente :)

asi0’s Bitcoin Guides

A lire