Skip to content

Commit

Permalink
Merge pull request #90 from Impa10r/v1.7.0
Browse files Browse the repository at this point in the history
v1.7.0
  • Loading branch information
Impa10r authored Oct 6, 2024
2 parents 7366ae9 + 4deb646 commit 8e320b3
Show file tree
Hide file tree
Showing 39 changed files with 5,070 additions and 1,697 deletions.
3 changes: 1 addition & 2 deletions .env
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
DEBUG=1
NETWORK="testnet"
NO_HTTPS="true"
NETWORK="testnet"
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
push.sh
cmd/psweb/__debug_bin*
.vscode
.vscode/launch.json
21 changes: 17 additions & 4 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
/* {
"name": "Launch Package",
"type": "go",
"request": "launch",
Expand All @@ -13,7 +13,20 @@
"program": "${workspaceFolder}/cmd/psweb/",
"showLog": false,
"envFile": "${workspaceFolder}/.env",
"args": ["-datadir", "/home/vlad/.peerswap_test"]
}
//"args": ["-datadir", "/home/vlad/.peerswap_t4"]
//"args": ["-datadir", "/home/vlad/.peerswap3"]
}, */
// sudo bash -c 'echo 0 > /proc/sys/kernel/yama/ptrace_scope'
// go install -tags cln -gcflags 'all=-N -l' ./cmd/psweb
// lcli -k plugin subcommand=stop plugin=/home/vlad/go/bin/psweb
// lcli -k plugin subcommand=start plugin=/home/vlad/go/bin/psweb
{
"name": "Attach to Package",
"type": "go",
"request": "attach",
"mode": "local",
"processId": "${command:pickProcess}",
"cwd": "${workspaceFolder}",
} */
]
}
}
2 changes: 1 addition & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
"problemMatcher": []
}
]
}
}
25 changes: 21 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
# Versions

## 1.7.0

- Enable CT discounted vsize with Elements v23.02.03+ on testnet
- Correct swap fee estimates for CT discounted vsize
- Implement confidential joint peg-in claims for CT discounts
- Add option to fund peg-ins with an external transaction
- LND: Facilitate exact (to 0.001) fee rates for peg-ins and BTC withdrawals
- Change Custom Message serialization from JSON to GOB
- Change Custom Message type from 42067 to 42065
- Improve accounting for initiated swap outs and failed swaps
- Show circular rebalancing volumes and costs on main screen
- Advertised balances: cap at remote channel balance
- Advertised balances: round down to 0 if below 100k sats (min swap amount)
- Advertised balances: enabled by default to discourage brute force discovery
- CLN: Refactor psweb as a plugin to use hooks and notifications
- Update keysend invite message to prospective peers

## 1.6.9

- Fix bitcoinswaps=true persisting in peerswap.conf on psweb restart
- Hot Fix bitcoinswaps=true persisting in peerswap.conf on psweb restart

## 1.6.8

Expand All @@ -16,7 +33,7 @@
- Fix AutoFees stop working bug
- Fix AutoFees applied on startup before forwards history has been downloaded
- LND 0.18: Fix AutoFees reacting to temporary balance increase due to pendinng HTLCs
- Highlight outputs to be used for peg-in or BTC withdrawal
- Highlight outputs to be used for pegging in or BTC withdrawal
- Warn when BTC swap-in amount is unlikely to cover chain fee
- Limit L-BTC swap-in amount to avoid excessive fee rate

Expand Down Expand Up @@ -239,7 +256,7 @@

## 1.2.3

- Add fee rate estimate for peg-in
- Add fee rate estimate for peg-ins
- Allow fee bumping peg-in tx (first CPFP, then RBF)

## 1.2.2
Expand All @@ -256,7 +273,7 @@
## 1.2.0

- Add Bitcoin balance and utxo list display
- Implement Liquid Peg-In functionality
- Implement Liquid Peg-in functionality
- Fallback to getblock.io if local Bitcoin Core unreachable

## 1.1.8
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
install-lnd:
go install ./cmd/psweb
@echo "psweb installed in $$(go env GOPATH)/bin/"

install-cln:
go install -tags cln ./cmd/psweb
go install -tags cln ./cmd/psweb
@echo "psweb installed in $$(go env GOPATH)/bin/"
@echo "Add 'plugin=$$(go env GOPATH)/bin/psweb' to $${HOME}/.lightning/config"
32 changes: 22 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# PeerSwap Web UI

A lightweight server-side rendered Web UI for PeerSwap, which allows trustless p2p submarine swaps Lightning<->BTC and Lightning<->Liquid. Also facilitates BTC->Liquid peg-ins and automatic channel fee management. PeerSwap with [Liquid](https://help.blockstream.com/hc/en-us/articles/900001408623-How-does-Liquid-Bitcoin-L-BTC-work) is a great cost efficient way to [rebalance lightning channels](https://medium.com/@goryachev/liquid-rebalancing-of-lightning-channels-2dadf4b2397a).
A lightweight Web UI for PeerSwap, which allows trustless p2p submarine swaps LightningBTC and LightningLiquid. Also facilitates BTCLiquid peg-ins and automatic channel fee management. PeerSwap with [Liquid](https://help.blockstream.com/hc/en-us/articles/900001408623-How-does-Liquid-Bitcoin-L-BTC-work) is a great cost efficient way to rebalance lightning channels.

### Disclaimer

Expand Down Expand Up @@ -48,23 +48,24 @@ Install and configure PeerSwap. Please consult [these instructions for LND](http

Clone the repository and build PeerSwap Web UI:

### LND:
### CLN Install:

```bash
git clone https://github.com/Impa10r/peerswap-web && \
cd peerswap-web && \
make -j$(nproc) install-lnd
make -j$(nproc) install-cln
```
PeerSwap Web UI is a now in your GOPATH (~/go/bin). To launch it as a CLN plugin, add ```plugin=/home/USER/go/bin/psweb``` to your ```~/.lightning/config``` file and restart ```lightningd``` (replace USER with your username).

### CLN:
### LND Install:

```bash
git clone https://github.com/Impa10r/peerswap-web && \
cd peerswap-web && \
make -j$(nproc) install-cln
make -j$(nproc) install-lnd
```

This will install `psweb` to your GOPATH (/home/USER/go/bin). You can check that it is working by running `psweb --version`. If not, add the path in ~/.profile and reload with `source .profile`.
This will install `psweb` to your GOPATH (~/go/bin).

To start psweb as a daemon, create a systemd service file as follows (replace USER with your username):

Expand Down Expand Up @@ -132,7 +133,8 @@ rm -rf peerswap-web && \
git clone https://github.com/Impa10r/peerswap-web && \
cd peerswap-web && \
make -j$(nproc) install-cln && \
sudo systemctl restart psweb
lightning-cli -k plugin subcommand=stop plugin=${HOME}/go/bin/psweb && \
lightning-cli -k plugin subcommand=start plugin=${HOME}/go/bin/psweb
```

## Automatic Liquid Swap-Ins
Expand Down Expand Up @@ -190,11 +192,11 @@ sudo systemctl stop psweb
sudo systemctl disable psweb
```

# Liquid Peg-In
# Liquid Peg-in

Update: From v1.2.0 this is handled via UI on the Bitcoin page.
Update: Since v1.2.0 this is handled via UI on the Bitcoin page.

To convert some BTC on your node into L-BTC you don't need any third party (but must run a full Bitcon node with txindex=1 enabled):
To convert some BTC on your node into L-BTC you don't need any third party (but must run a full Bitcon node with txindex=1, or manually provide block hash from mempool.space for ```getrawtransaction``` and ```gettxoutproof```):

1. Generate a special BTC address: ```elements-cli getpeginaddress```. Save claim_script for later.
2. Send BTC onchain: ```lncli sendcoins --amt <sats to peg in> -addr <mainchain_address from step 1> --sat_per_vbyte <from mempool>```
Expand All @@ -215,6 +217,16 @@ alias ecli="docker exec -it elements_node_1 elements-cli -rpcuser=elements -rpcp

(lookup Elements and Bitcoin rpc passwords in pswebconfig.com)

## Confidential Liquid Peg-in

Elements Core v23.2.2 introduced vsize discount for confidential transactions. Now sending a Liquid payment with a blinded amount costs the same or cheaper than a publicly visible (explicit) one. For example, claiming a peg-in with ```elements-cli claimpegin``` costs about 45 sats, but it is possible to manually construct the same transaction (```elements-cli createrawtransaction```) with confidential destination address, blind and sign it, then post and pay a lower fee. However, from privacy perspective, blinding a single peg-in claim makes little sense. The linked Bitcoin UTXO will still show the explicit amount, so it is easily traceable to your new Liquid address. To achieve a truly confidential peg-in, it is necessary to mix two or more independent claims into one single transaction, a-la CoinJoin.

In v1.7.0 PSWeb implemented such "ClaimJoin". If you opt in when starting your peg-in, your node will send invitations to all other PSWeb nodes to join in while you wait for your 102 confirmations. To join your claim, a peer should opt in while starting his own peg-in. A node responds to an invitation by anonymously sending details of its peg-in funding transaction, once it confirms, to the initiator. Peers don't know which specific node initiated the ClaimJoin and who else will be joining. The initiator also doesn't know public Ids of the nodes that responded. All communication happens blindly via single use public/private key pairs (secp256k1). Nodes who do not directly participate act as p2p relays for the encrypted messages, not being able to read them and not knowing the sources and the final destinations. This way our ClaimJoin coordination is fully confidential and not limited to direct peers.

When all N peg-ins mature, the initiator node prepares one large PSET with N peg-in inputs and N CT outputs, shuffled randomly, and sends it secuentially to all participants: first to blind Liquid outputs and then to sign peg-in inputs. Before blinding/signing and returning the PSET, each joiner verifies that his output address is there for the correct amount (allowing for a small fee haircut). Upto 10 claims can be joined this way, to fit into one custom message (64kb). The price for such privacy is time. For the initiator, the wait can take upto 34 hours if the final peer joins at block 101. For that last joiner the wait will be the same 17 hours as for a standard peg-in. If the total fee cannot be divided equally, the last joiner pays slightnly more as an incentive to join earlier next time. In practice, the blinding and signing round may need to be done twice: first to find out the exact discounted vsize of the final transaction, then to set the exact total fee at 0.1 sat/vb.

The process bears no risk to the participants. If any joiner becomes unresponsive during the blinding/signing round, he is automatically kicked out. If the initiator fails to complete the process, each joiner reverts to a standard single peg-in claim 10 blocks after the final maturity. As the last resort, if your PSWeb dies completely, you can always [claim your peg-in manually](#liquid-pegin) with ```elements-cli```. All the necessary details will be in your PSWeb log. Your claim script and peg-in txid can only be used with your own Liquid wallet's private key. Blinding and signing your part happens locally on your node, no sensitive info is transmitted outside at any point.

# Support

Join PeerSwap Discord channel at [PeerSwap.dev](https://peerswap.dev).
14 changes: 9 additions & 5 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@

PeerSwap Web UI offers secure communication with the clients via mTLS. When HTTPS option is enabled, a self-signed root Certificate Authority CA.crt is created first. It is then used to sign two certificates: server.crt and client.p12. Both CA.crt and client.p12 need to be installed on the client's devices, to bootstrap a secure connection with the server. The certificates are used during the TLS handshake to authenticate the server to the client and vice versa. Our communication channel is now encrypted and no third party can eavesdrop or connect to the server.

For networks with small attack surfaces it is possible to opt-in for a less secure setup with a single client password instead of the client.crt certificate. In this case a session browser cookie is used to maintain authentication status. Warning: without CA certificate installed on the user device the browser will display warnings that the server cannot be trusted. This is because MITM attack is possible, where another server pretends to be PeerSwap Web UI to phish the password. Always make sure to install the CA certificate when opting for password authentication.
For networks with a small attack surface it is possible to opt-in for a less secure setup with a single client password instead of the client.crt certificate. In this case a session browser cookie is used to maintain authentication status. Warning: without CA certificate installed on the user device the browser will display warnings that the server cannot be trusted. This is because MITM attack is possible, where another server pretends to be PeerSwap Web UI to phish the password. Always make sure to install the CA certificate when opting for password authentication.

## Privacy Disclosure

There is no centralized server. PeerSwap Web UI does not share your private data with the contributors. The software, however, may utilize API endpoints of github.com, mempool.space, telegram.org and getblock.io to send and receive certain information. You can avoid leaking your IP address to these websites by specifying a Tor proxy on the Configuration page. You may also provide URL of a locally installed Mempool server.
There is no centralized server. PeerSwap Web UI does not share your private data with the contributors nor send them any donations. The software, however, may utilize API endpoints of github.com, mempool.space, telegram.org and getblock.io to send and receive certain information. You can avoid leaking your IP address to these websites by specifying a Tor proxy on the Configuration page. You may also provide URL of a locally installed Mempool server.

Getblock runs a publicly available Bitcoin Core server. It is used as a fallback when your local installation is not accessible via API or is not configured to enable Liquid peg-ins. The default account is anonymous, but some contributors may have access to monitor usage statistics. You may opt out by registering your own free account at getblock.io and providing its endpoint, or by running your local suitably configured Bitcoin Core software.
Getblock runs a publicly available Bitcoin Core server. It is used as a fallback when your local installation is not accessible via API or is not configured to facilitate Liquid peg-ins. The default account is anonymous, but some contributors may have access to monitor aggregate usage statistics. You may opt out by registering your own free account at getblock.io and providing its endpoint, or by running your suitably configured Bitcoin Core locally.

## Reporting a Vulnerability
BTC and L-BTC on-chain balances are advertised to direct peers only to the extent they can be discovered by brute force. A peer can attempt smaller and smaller size swap-outs until one works, resulting in many annoying failed swaps in the history. He cannot do swaps larger than his local channel balance and smaller than 100k sats. To mirror that, your advertised balance will be capped at your remote channel balance and rounded down to 0 if below 100k. This way you will not disclose more than what is potentially discoverable. Advertising balances does not reduce your privacy and is therefore enabled by default (but can be easily disabled).

If you discover any vulnerability, please report it discretely to contributors.
## Reporting Vulnerability

If you discover a vulnerability, please report it to email [email protected].

Sensitive information should be encrypted with this [PGP key](https://gist.github.com/Impa10r/33b09271ac8ae3f1545cf78318369810) (fingerprint FDD6 C5A6 EAD5 F4F2 3404 27FB 770B CCFB 70F3 133D).
94 changes: 90 additions & 4 deletions cmd/psweb/bitcoin/bitcoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package bitcoin

import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"math"
"net/http"
"net/url"
"time"
Expand Down Expand Up @@ -207,6 +207,57 @@ func GetRawTransaction(txid string, result *Transaction) (string, error) {
return raw, nil
}

type FeeInfo struct {
Feerate float64 `json:"feerate"`
Blocks int `json:"blocks"`
}

// Estimate sat/vB fee rate from bitcoin core
func EstimateSatvB(targetConf uint) float64 {
client := BitcoinClient()
service := &Bitcoin{client}

params := []interface{}{targetConf}

r, err := service.client.call("estimatesmartfee", params, "")
if err = handleError(err, &r); err != nil {
return 0
}

var feeInfo FeeInfo

err = json.Unmarshal([]byte(r.Result), &feeInfo)
if err != nil {
log.Printf("GetRawTransaction unmarshall raw: %v", err)
return 0
}

return math.Round(feeInfo.Feerate * 100_000)
}

// returns block hash
func GetBlockHash(block uint32) (string, error) {
client := BitcoinClient()
service := &Bitcoin{client}
params := &[]interface{}{block}

r, err := service.client.call("getblockhash", params, "")
if err = handleError(err, &r); err != nil {
log.Printf("GetBlockHash: %v", err)
return "", err
}

var response string

err = json.Unmarshal([]byte(r.Result), &response)
if err != nil {
log.Printf("GetBlockHash unmarshall: %v", err)
return "", err
}

return response, nil
}

func GetTxOutProof(txid string) (string, error) {
client := BitcoinClient()
service := &Bitcoin{client}
Expand Down Expand Up @@ -293,6 +344,21 @@ func DecodeRawTransaction(hexstring string) (*Transaction, error) {
return &transaction, nil
}

func FindVout(hexTx string, amount uint64) (uint, error) {
tx, err := DecodeRawTransaction(hexTx)
if err != nil {
return 0, err
}

for i, o := range tx.Vout {
if uint64(o.Value*100_000_000) == amount {
return uint(i), nil
}
}

return 0, fmt.Errorf("vout not found")
}

func SendRawTransaction(hexstring string) (string, error) {
client := BitcoinClient()
service := &Bitcoin{client}
Expand All @@ -316,9 +382,7 @@ func SendRawTransaction(hexstring string) (string, error) {
}

// extracts Fee from PSBT
func GetFeeFromPsbt(psbtBytes *[]byte) (float64, error) {
base64string := base64.StdEncoding.EncodeToString(*psbtBytes)

func GetFeeFromPsbt(base64string string) (float64, error) {
client := BitcoinClient()
service := &Bitcoin{client}

Expand All @@ -341,3 +405,25 @@ func GetFeeFromPsbt(psbtBytes *[]byte) (float64, error) {

return fee, nil
}

func CreatePSBT(params interface{}) (string, error) {

client := BitcoinClient()
service := &Bitcoin{client}

r, err := service.client.call("createpsbt", params, "")
if err = handleError(err, &r); err != nil {
log.Printf("Failed to create PSET: %v", err)
return "", err
}

var response string

err = json.Unmarshal([]byte(r.Result), &response)
if err != nil {
log.Printf("CreatePSET unmarshall: %v", err)
return "", err
}

return response, nil
}
10 changes: 5 additions & 5 deletions cmd/psweb/config/cln.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@ func loadDefaults(home, dataDir string) {
if dataDir == "" {
Config.LightningDir = filepath.Join(home, ".lightning")
} else {
// Drop the last folder
Config.LightningDir = dataDir
}

Config.RpcHost = filepath.Join(Config.LightningDir, "bitcoin")
if Config.Chain == "testnet" {
Config.RpcHost = filepath.Join(Config.LightningDir, "testnet")
Config.DataDir = filepath.Join(Config.RpcHost, "peerswap")
} else {
Config.RpcHost = filepath.Join(Config.LightningDir, "bitcoin")
Config.DataDir = filepath.Join(Config.RpcHost, "peerswap")
}
Config.DataDir = filepath.Join(Config.RpcHost, "peerswap")
}

// CLN-specific load from Peerswap config
Expand All @@ -44,7 +43,7 @@ func LoadPS() {
port := GetPeerswapCLNSetting("Bitcoin", "rpcport")
if port == "" {
port = "8332"
if os.Getenv("NETWORK") == "testnet" {
if Config.Chain == "testnet" {
port = "18332"
}
}
Expand Down Expand Up @@ -124,6 +123,7 @@ func SavePS() {
rpchost := GetPeerswapCLNSetting("Bitcoin", "rpchost")
if rpchost != "" {
t += setPeerswapVariable("Bitcoin", "rpchost", "", "", "", true)
t += setPeerswapVariable("Bitcoin", "rpcport", "", "", "", false)
}
cookiefilepath := GetPeerswapCLNSetting("Bitcoin", "cookiefilepath")
if cookiefilepath != "" {
Expand Down
Loading

0 comments on commit 8e320b3

Please sign in to comment.