Skip to content

Commit

Permalink
Merge pull request #98 from Impa10r/v1.7.7
Browse files Browse the repository at this point in the history
v1.7.7
  • Loading branch information
Impa10r authored Feb 6, 2025
2 parents 69fb29f + 52ee2d8 commit 5b99618
Show file tree
Hide file tree
Showing 11 changed files with 505 additions and 469 deletions.
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"program": "${workspaceFolder}/cmd/psweb/",
"showLog": false,
//"envFile": "${workspaceFolder}/.env",
//"args": ["-datadir", "/home/vlad/.peerswap_t4"]
//"args": ["-datadir", "/home/vlad/.peerswap2"]
//"args": ["-datadir", "/home/vlad/.peerswap2_t4"]
"args": ["-datadir", "/home/vlad/.peerswap2"]
},
// sudo bash -c 'echo 0 > /proc/sys/kernel/yama/ptrace_scope'
// go install -tags cln -gcflags 'all=-N -l' ./cmd/psweb
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Versions

## 1.7.7

- Fix Auto Fee not working for channels with no outbound forwards
- Better indication when all channels with a peer are inactive
- Catch Bitcoin Core and Elements Core RPC errors
- Fix telegram bot panic on wrong token or chat id
- Allow Bitcoin fee rates with 0.01 precision
- Reserve 1200 sats of LBTC balance to save on Elements fees
- Show confidential peg-in join time limit on peg-in form
- Allow updating txid of externally funded peg-in when not found

## 1.7.6

- Fix BTC to sats rounding bug preventing claim init or join
Expand Down
5 changes: 3 additions & 2 deletions cmd/psweb/bitcoin/bitcoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ func handleError(err error, r *rpcResponse) error {
if err != nil {
return err
}
if r.Err != nil {
return r.Err
if r != nil && r.Err != nil {
return fmt.Errorf(r.Err.Message)
}

return nil
Expand Down Expand Up @@ -331,6 +331,7 @@ func DecodeRawTransaction(hexstring string) (*Transaction, error) {
r, err := service.client.call("decoderawtransaction", params, "")
if err = handleError(err, &r); err != nil {
log.Printf("DecodeRawTransaction: %v", err)
log.Printf("Hex: %s", hexstring)
return nil, err
}

Expand Down
32 changes: 20 additions & 12 deletions cmd/psweb/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ func peerHandler(w http.ResponseWriter, r *http.Request) {
// to find a channel for swap-in
maxRemoteBalance := uint64(0)
maxRemoteBalanceIndex := 0
isOnline := false

// get routing stats
for i, ch := range peer.Channels {
Expand All @@ -383,6 +384,8 @@ func peerHandler(w http.ResponseWriter, r *http.Request) {
}

info.Active = ch.GetActive()
isOnline = isOnline || info.Active

info.LocalPct = info.LocalBalance * 100 / info.Capacity
channelInfo = append(channelInfo, info)

Expand Down Expand Up @@ -435,7 +438,7 @@ func peerHandler(w http.ResponseWriter, r *http.Request) {
swapFeeReserveBTC := uint64(math.Ceil(bitcoinFeeRate * 350))

// arbitrary haircut to avoid 'no matching outgoing channel available'
maxLiquidSwapIn := min(int64(satAmount)-int64(swapFeeReserveLBTC(len(utxosLBTC))), int64(maxRemoteBalance)-10000)
maxLiquidSwapIn := min(int64(satAmount)-SWAP_LBTC_RESERVE, int64(maxRemoteBalance)-10000)
if maxLiquidSwapIn < 100_000 {
maxLiquidSwapIn = 0
}
Expand All @@ -451,7 +454,7 @@ func peerHandler(w http.ResponseWriter, r *http.Request) {
} else {
peerLiquidBalance = formatWithThousandSeparators(ptr.Amount)
}
maxLiquidSwapOut = uint64(max(0, min(int64(maxLocalBalance)-SWAP_OUT_CHANNEL_RESERVE, int64(ptr.Amount)-int64(swapFeeReserveLBTC(1)))))
maxLiquidSwapOut = uint64(max(0, min(int64(maxLocalBalance)-SWAP_OUT_CHANNEL_RESERVE, int64(ptr.Amount))))
} else {
maxLiquidSwapOut = uint64(max(0, int64(maxLocalBalance)-SWAP_OUT_CHANNEL_RESERVE))
}
Expand Down Expand Up @@ -591,6 +594,9 @@ func peerHandler(w http.ResponseWriter, r *http.Request) {
SelectedChannel uint64
HasDiscountedvSize bool
RedColor string
IsOnline bool
AnchorReserve uint64
LiquidReserve uint64
}

redColor := "red"
Expand Down Expand Up @@ -645,6 +651,9 @@ func peerHandler(w http.ResponseWriter, r *http.Request) {
SelectedChannel: selectedChannel,
HasDiscountedvSize: hasDiscountedvSize,
RedColor: redColor,
IsOnline: isOnline,
AnchorReserve: ANCHOR_RESERVE,
LiquidReserve: SWAP_LBTC_RESERVE,
}

// executing template named "peer"
Expand Down Expand Up @@ -714,6 +723,7 @@ func bitcoinHandler(w http.ResponseWriter, r *http.Request) {
ClaimJoinStatus string
HasClaimJoinPending bool
ClaimJoinETA int
ClaimJointTimeLimit string
}

btcBalance := ln.ConfirmedWalletBalance(cl)
Expand Down Expand Up @@ -752,14 +762,16 @@ func bitcoinHandler(w http.ResponseWriter, r *http.Request) {
duration := time.Duration(10*(int32(peginBlocks)-confs)) * time.Minute
maxConfs := int32(peginBlocks)
cjETA := 34
cjTimeLimit := ""

bh := int32(ln.GetBlockHeight())
currentBlockHeight := int32(ln.GetBlockHeight())
if ln.MyRole != "none" {
target := int32(ln.ClaimBlockHeight)
maxConfs = target - bh + confs
duration = time.Duration(10*(target-bh)) * time.Minute
maxConfs = target - currentBlockHeight + confs
duration = time.Duration(10*(target-currentBlockHeight)) * time.Minute
} else if ln.ClaimJoinHandler != "" {
cjETA = int((int32(ln.JoinBlockHeight) - bh + int32(peginBlocks)) / 6)
cjETA = int((int32(ln.JoinBlockHeight) - currentBlockHeight + int32(peginBlocks)) / 6)
cjTimeLimit = time.Now().Add(time.Duration(10*(ln.JoinBlockHeight-uint32(currentBlockHeight))) * time.Minute).Format("3:04 PM")
}

progress := confs * 100 / int32(maxConfs)
Expand Down Expand Up @@ -801,6 +813,7 @@ func bitcoinHandler(w http.ResponseWriter, r *http.Request) {
IsClaimJoin: config.Config.PeginClaimJoin,
ClaimJoinStatus: ln.ClaimStatus,
HasClaimJoinPending: ln.ClaimJoinHandler != "",
ClaimJointTimeLimit: cjTimeLimit,
ClaimJoinETA: cjETA,
}

Expand Down Expand Up @@ -1004,7 +1017,7 @@ func peginHandler(w http.ResponseWriter, r *http.Request) {
telegramSendMessage("⏰ Started peg in " + formatWithThousandSeparators(uint64(res.AmountSat)) + " sats. Time left: " + formattedDuration + ". TxId: `" + res.TxId + "`")
} else {
log.Println("BTC withdrawal pending, TxId:", res.TxId, "RawHex:", res.RawHex)
telegramSendMessage("BTC withdrawal pending: " + formatWithThousandSeparators(uint64(res.AmountSat)) + " sats. TxId: `" + res.TxId + "`")
telegramSendMessage("⛓️ BTC withdrawal pending: " + formatWithThousandSeparators(uint64(res.AmountSat)) + " sats. TxId: `" + res.TxId + "`")
}
config.Config.PeginAmount = res.AmountSat
config.Config.PeginTxId = res.TxId
Expand Down Expand Up @@ -1833,11 +1846,6 @@ func submitHandler(w http.ResponseWriter, r *http.Request) {

switch action {
case "externalPeginTxId":
if config.Config.PeginTxId != "external" {
redirectWithError(w, r, "/bitcoin?", errors.New("not expected"))
return
}

if r.FormValue("externalPeginCancel") != "" {
config.Config.PeginTxId = ""
config.Config.PeginClaimJoin = false
Expand Down
4 changes: 2 additions & 2 deletions cmd/psweb/liquid/elements.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ func handleError(err error, r *rpcResponse) error {
if err != nil {
return err
}
if r.Err != nil {
return r.Err
if r != nil && r.Err != nil {
return fmt.Errorf(r.Err.Message)
}

return nil
Expand Down
2 changes: 1 addition & 1 deletion cmd/psweb/ln/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ func calculateAutoFee(channelId uint64, params *AutoFeeParams, liqPct int, oldFe
// must be definitely above threshold and cool-off period passed
if liqPct > params.LowLiqPct && lastUpdate < time.Now().Add(-time.Duration(params.CoolOffHours)*time.Hour).Unix() {
// check the inactivity period
if ts, ok := LastForwardTS.Read(channelId); ok && ts < time.Now().AddDate(0, 0, -params.InactivityDays).Unix() {
if ts, ok := LastForwardTS.Read(channelId); !ok || ok && ts < time.Now().AddDate(0, 0, -params.InactivityDays).Unix() {
// decrease the fee
newFee -= params.InactivityDropPPM
newFee = newFee * (100 - params.InactivityDropPct) / 100
Expand Down
3 changes: 2 additions & 1 deletion cmd/psweb/ln/lnd.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ finalize:

decoded, err := bitcoin.DecodeRawTransaction(hex.EncodeToString(rawTx))
if err != nil {
//log.Println("Funded PSBT:", hex.EncodeToString(psbtBytes))
return nil, err
}

Expand All @@ -345,7 +346,7 @@ finalize:
requiredFee := int64(feeRate * float64(decoded.VSize))

if requiredFee != toSats(feePaid) {
if pass < 5 {
if pass < 3 || requiredFee > toSats(feePaid) {
log.Println("Trying to fix fee paid", toSats(feePaid), "vs required", requiredFee)

releaseOutputs(cl, utxos, &lockId)
Expand Down
55 changes: 22 additions & 33 deletions cmd/psweb/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,14 @@ import (

const (
// App VERSION tag
VERSION = "v1.7.6"
// Swap Out reserve
SWAP_OUT_CHANNEL_RESERVE = 10000
// Elements v23.02.03 introduced vsize discount enabled on testnet as default
VERSION = "v1.7.7"
// Swap Out reserve to deduct from channel local balance
SWAP_OUT_CHANNEL_RESERVE = 10_000
// https://github.com/ElementsProject/peerswap/pull/304#issuecomment-2303931071
SWAP_LBTC_RESERVE = 1_200
// Unusable BTC balance
ANCHOR_RESERVE = 25_000
// assume creatediscountct=1 for mainnet in elements.conf
ELEMENTS_DISCOUNTED_VSIZE_VERSION = 230203
)

Expand Down Expand Up @@ -1038,12 +1042,12 @@ func convertSwapsToHTMLTable(swaps []*peerswaprpc.PrettyPrintSwap, nodeId string
if cost != 0 {
totalCost += cost
ppm := cost * 1_000_000 / int64(swap.Amount)
table += " <span title=\"Swap +profit/-cost, sats. PPM: "
table += " <span title=\"Swap "

if cost < 0 {
table += formatSigned(-ppm) + "\">+"
table += "profit, sats. PPM: " + formatSigned(-ppm) + "\">+"
} else {
table += formatSigned(ppm) + "\">"
table += "cost, sats. PPM: " + formatSigned(ppm) + "\">"
}
table += formatSigned(-cost) + "</span>"
}
Expand Down Expand Up @@ -1129,7 +1133,6 @@ func checkPegin() {
// invitation expired
ln.ClaimStatus = "No ClaimJoin peg-in is pending"
log.Println("Invitation expired from", ln.ClaimJoinHandler)
telegramSendMessage("🧬 ClaimJoin Invitation expired")

ln.ClaimJoinHandler = ""
db.Save("ClaimJoin", "ClaimStatus", ln.ClaimStatus)
Expand All @@ -1139,13 +1142,11 @@ func checkPegin() {
if config.Config.PeginTxId == "" {
// send telegram if received new ClaimJoin invitation
if peginInvite != ln.ClaimJoinHandler {
t := "🧬 There is a ClaimJoin peg-in pending"
if ln.ClaimJoinHandler == "" {
t = "🧬 ClaimJoin peg-in has ended"
} else {
t := "⌚ ClaimJoin invitation has expired"
if ln.ClaimJoinHandler != "" {
duration := time.Duration(10*(ln.JoinBlockHeight-currentBlockHeight)) * time.Minute
formattedDuration := time.Time{}.Add(duration).Format("15h 04m")
t += ", time limit to join: " + formattedDuration
timeLimit := time.Now().Add(duration).Format("3:04 PM")
t = "🧬 Invitation to join a confidential peg-in before " + timeLimit
}
if telegramSendMessage(t) {
peginInvite = ln.ClaimJoinHandler
Expand Down Expand Up @@ -1319,7 +1320,7 @@ func cacheAliases() {
// The goal is to spend maximum available liquid
// To rebalance a channel with high enough historic fee PPM
func findSwapInCandidate(candidate *SwapParams) error {
minAmount := config.Config.AutoSwapThresholdAmount - swapFeeReserveLBTC(10) // assume 10 UTXOs
minAmount := config.Config.AutoSwapThresholdAmount - SWAP_LBTC_RESERVE
minPPM := config.Config.AutoSwapThresholdPPM

client, cleanup, err := ps.GetClient(config.Config.RpcHost)
Expand Down Expand Up @@ -1500,7 +1501,7 @@ func executeAutoSwap() {
return
}

amount = min(amount, satAmount-swapFeeReserveLBTC(10)) // assume 10 UTXOs
amount = min(amount, satAmount-SWAP_LBTC_RESERVE)

// execute swap
autoSwapId, err = ps.SwapIn(client, amount, candidate.ChannelId, "lbtc", false)
Expand Down Expand Up @@ -1795,14 +1796,14 @@ func advertiseBalances() {

bitcoinBalance := uint64(ln.ConfirmedWalletBalance(cl))
// haircut by anchor reserve
if bitcoinBalance >= 25000 {
bitcoinBalance -= 25000
if bitcoinBalance >= ANCHOR_RESERVE {
bitcoinBalance -= ANCHOR_RESERVE
}

liquidBalance := res2.GetSatAmount()
// Elements fee bug does not permit sending the whole balance, haircut it
if liquidBalance >= 2000 {
liquidBalance -= 2000
if liquidBalance >= SWAP_LBTC_RESERVE {
liquidBalance -= SWAP_LBTC_RESERVE
}

cutOff := time.Now().AddDate(0, 0, -1).Unix() - 120
Expand All @@ -1812,7 +1813,7 @@ func advertiseBalances() {
maxBalance := uint64(0)
if ln.AdvertiseBitcoinBalance || ln.AdvertiseLiquidBalance {
for _, ch := range peer.Channels {
maxBalance = max(maxBalance, ch.RemoteBalance)
maxBalance = max(maxBalance, ch.RemoteBalance-SWAP_OUT_CHANNEL_RESERVE)
}
}

Expand Down Expand Up @@ -1932,15 +1933,3 @@ func pollBalances() {
log.Println("Polled peers for balances")
}
}

// depends on number of UTXOs
func swapFeeReserveLBTC(numUTXOs int) uint64 {
if hasDiscountedvSize {
n := numUTXOs*7 + 20 // better estimate for lots of UTXOs
if n < 75 {
n = 75 // peerswap assumes 75 sats
}
return uint64(n)
}
return 300
}
15 changes: 12 additions & 3 deletions cmd/psweb/telegram.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ func telegramStart() {
// Try saved chatId
chatId = config.Config.TelegramChatId
if chatId > 0 {
telegramConnect()
if !telegramConnect() {
return
}
}

updates := bot.GetUpdatesChan(u)
Expand Down Expand Up @@ -158,7 +160,7 @@ func telegramStart() {
}
}

func telegramConnect() {
func telegramConnect() bool {
if telegramSendMessage("📟 PeerSwap connected") {
// successfully connected
cmdCfg := tgbotapi.NewSetMyCommands(
Expand Down Expand Up @@ -187,9 +189,16 @@ func telegramConnect() {
config.Config.TelegramChatId = chatId
config.Save()
} else {
chatId = 0
if chatId > 0 {
chatId = 0
config.Config.TelegramChatId = chatId
config.Save()
log.Println("Chat Id was reset. Use /start in Telegram to start the bot.")
}
bot = nil
return false
}
return true
}

func telegramSendMessage(msgText string) bool {
Expand Down
Loading

0 comments on commit 5b99618

Please sign in to comment.