Merge pull request #14 from ziggie1984/econ_ratio_max_ppm

pull/18/head
rkfg 2 years ago committed by GitHub
commit fc246d16bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -48,13 +48,10 @@ rebalance-lnd](https://github.com/accumulator/rebalance-lnd).
--pto= channels with less than this outbound liquidity percentage will be considered as target channels
-p, --perc= use this value as both pfrom and pto from above
-a, --amount= amount to rebalance
--rel-amount-to= calculate amount as the target channel capacity fraction (for example, 0.2 means you want to
achieve at most 20% target channel local balance)
--rel-amount-from= calculate amount as the source channel capacity fraction (for example, 0.2 means you want to
achieve at most 20% source channel remote balance)
-r, --econ-ratio= economical ratio for fee limit calculation as a multiple of target channel fee (for example,
0.5 means you want to pay at max half the fee you might earn for routing out of the target
channel)
--rel-amount-to= calculate amount as the target channel capacity fraction (for example, 0.2 means you want to achieve at most 20% target channel local balance)
--rel-amount-from= calculate amount as the source channel capacity fraction (for example, 0.2 means you want to achieve at most 20% source channel remote balance)
-r, --econ-ratio= economical ratio for fee limit calculation as a multiple of target channel fee (for example, 0.5 means you want to pay at max half the fee you might earn for routing out of the target channel)
--econ-ratio-max-ppm= limits the max fee ppm for a rebalance when using econ ratio
-F, --fee-limit-ppm= don't consider the target channel fee and use this max fee ppm instead (can rebalance at a loss, be careful)
-l, --lost-profit also consider the outbound channel fees when looking for profitable routes so that outbound_fee+inbound_fee < route_fee
-b, --probe-steps= if the payment fails at the last hop try to probe lower amount using this many steps

@ -7,9 +7,12 @@ import (
"log"
"math"
"math/rand"
"strconv"
"strings"
"time"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwire"
)
func formatChannelPair(a, b uint64) string {
@ -144,3 +147,32 @@ func (r *regolancer) addFailedRoute(from, to uint64) {
r.failureCache[k] = failedRoute{channelPair: r.channelPairs[k], expiration: &t}
delete(r.channelPairs, k)
}
func parseScid(chanId string) int64 {
elements := strings.Split(strings.ToLower(chanId), "x")
blockHeight, err := strconv.ParseInt(elements[0], 10, 24)
if err != nil {
log.Fatalf("error: not able to parse Blockheight of ShortChannelID %s, %s ", chanId, err)
}
txIndex, err := strconv.ParseInt(elements[1], 10, 24)
if err != nil {
log.Fatalf("error: not able to parse TxIndex of ShortChannelID %s, %s ", chanId, err)
}
txPosition, err := strconv.ParseInt(elements[2], 10, 32)
if err != nil {
log.Fatalf("error: not able to parse txPosition of ShortChannelID %s, %s ", chanId, err)
}
var scId lnwire.ShortChannelID
scId.BlockHeight = uint32(blockHeight)
scId.TxIndex = uint32(txIndex)
scId.TxPosition = uint16(txPosition)
return int64(scId.ToUint64())
}

@ -13,26 +13,28 @@
"stat": "stats.csv",
"lost_profit": true,
"exclude_channels_in": [
821913529170526209,
821280210377179136
"821913529170526209",
"821280210377179136"
],
"exclude_channels_out": [
821264817346314240,
818108119373578240
"821264817346314240",
"818108119373578240",
"757806x673x1"
],
"exclude_channels": [
802741344926629888
"802741344926629888"
],
"exclude_nodes": [
"0363749dbdb99321177082935bf7c36978fc46c725c5c452d48d721ab31e7682c2",
"026165850492521f4ac8abd9bd8088123446d126f648ca35e60f88177dc149ceb2"
],
"from": [
794863344113680384,
806323553821196289
"794863344113680384",
"806323553821196289"
],
"to": [
822784342311501826,
821280210373779139
"822784342311501826",
"821280210373779139",
"757806x673x1"
]
}

@ -1,33 +1,35 @@
connect = "192.168.178.22"
macaroon_dir = "."
macaroon_dir = "."
macaroon_filename = "admin.macaroon"
"network"= "mainnet"
"tlscert"= "./tls.cert"
"econ_ratio"= 0.4
"amount"= 100000
"min_amount"= 50000
"probe_steps"= 5
"pfrom"= 40
"pto"= 40
"stat"= "stats.csv"
"network" = "mainnet"
"tlscert" = "./tls.cert"
"econ_ratio" = 0.4
"amount" = 10000
"min_amount" = 1000
"probe_steps" = 5
"pfrom" = 40
"pto" = 40
"stat" = "stats.csv"
lost_profit = true
"exclude_channels_in"= [
# River Financial
821913529170526209,
#southxchange
821280210377179136
]
"exclude_channels_out"= [
"exclude_channels_in" = [
# River Financial
"821913529170526209",
#southxchange
"821280210377179136",
]
"exclude_channels_out" = [
#nicehash
794863344113680384,
"722924x1760x0",
#bfx-lnd1
817004209683890177,
"817004209683890177",
#HOPPINGSQUIRREL
806323553821196289
"806323553821196289"
#LOOP
"757806x673x1"
]
"exclude_channels"= [
#Kraken
830099393243185153
"830099393243185153"
]
"exclude_nodes"= [
@ -37,12 +39,12 @@ lost_profit = true
"026165850492521f4ac8abd9bd8088123446d126f648ca35e60f88177dc149ceb2"
]
"to" = [
794863344113680384,
817004209683890177,
806323553821196289
"794863344113680384",
"817004209683890177",
"806323553821196289"
]
"from" = [
821280210377179137,
821280210377179136
"821280210377179137",
"821280210377179136"
]

@ -6,6 +6,7 @@ import (
"log"
"math/rand"
"os"
"strconv"
"strings"
"time"
@ -30,16 +31,17 @@ type configParams struct {
RelAmountTo float64 `long:"rel-amount-to" description:"calculate amount as the target channel capacity fraction (for example, 0.2 means you want to achieve at most 20% target channel local balance)"`
RelAmountFrom float64 `long:"rel-amount-from" description:"calculate amount as the source channel capacity fraction (for example, 0.2 means you want to achieve at most 20% source channel remote balance)"`
EconRatio float64 `short:"r" long:"econ-ratio" description:"economical ratio for fee limit calculation as a multiple of target channel fee (for example, 0.5 means you want to pay at max half the fee you might earn for routing out of the target channel)" json:"econ_ratio" toml:"econ_ratio"`
EconRatioMaxPPM int64 `long:"econ-ratio-max-ppm" description:"limits the max fee ppm for a rebalance when using econ ratio" json:"econ_ratio_max_ppm" toml:"econ_ratio_max_ppm"`
FeeLimitPPM int64 `short:"F" long:"fee-limit-ppm" description:"don't consider the target channel fee and use this max fee ppm instead (can rebalance at a loss, be careful)" json:"fee_limit_ppm" toml:"fee_limit_ppm"`
LostProfit bool `short:"l" long:"lost-profit" description:"also consider the outbound channel fees when looking for profitable routes so that outbound_fee+inbound_fee < route_fee" json:"lost_profit" toml:"lost_profit"`
ProbeSteps int `short:"b" long:"probe-steps" description:"if the payment fails at the last hop try to probe lower amount using this many steps" json:"probe_steps" toml:"probe_steps"`
MinAmount int64 `long:"min-amount" description:"if probing is enabled this will be the minimum amount to try" json:"min_amount" toml:"min_amount"`
ExcludeChannelsIn []uint64 `short:"i" long:"exclude-channel-in" description:"don't use this channel as incoming (can be specified multiple times)" json:"exclude_channels_in" toml:"exclude_channels_in"`
ExcludeChannelsOut []uint64 `short:"o" long:"exclude-channel-out" description:"don't use this channel as outgoing (can be specified multiple times)" json:"exclude_channels_out" toml:"exclude_channels_out"`
ExcludeChannels []uint64 `short:"e" long:"exclude-channel" description:"don't use this channel at all (can be specified multiple times)" json:"exclude_channels" toml:"exclude_channels"`
ExcludeChannelsIn []string `short:"i" long:"exclude-channel-in" description:"don't use this channel as incoming (can be specified multiple times)" json:"exclude_channels_in" toml:"exclude_channels_in"`
ExcludeChannelsOut []string `short:"o" long:"exclude-channel-out" description:"don't use this channel as outgoing (can be specified multiple times)" json:"exclude_channels_out" toml:"exclude_channels_out"`
ExcludeChannels []string `short:"e" long:"exclude-channel" description:"don't use this channel at all (can be specified multiple times)" json:"exclude_channels" toml:"exclude_channels"`
ExcludeNodes []string `short:"d" long:"exclude-node" description:"don't use this node for routing (can be specified multiple times)" json:"exclude_nodes" toml:"exclude_nodes"`
ToChannel []uint64 `long:"to" description:"try only this channel as target (should satisfy other constraints too; can be specified multiple times)" json:"to" toml:"to"`
FromChannel []uint64 `long:"from" description:"try only this channel as source (should satisfy other constraints too; can be specified multiple times)" json:"from" toml:"from"`
ToChannel []string `long:"to" description:"try only this channel as target (should satisfy other constraints too; can be specified multiple times)" json:"to" toml:"to"`
FromChannel []string `long:"from" description:"try only this channel as source (should satisfy other constraints too; can be specified multiple times)" json:"from" toml:"from"`
AllowUnbalanceFrom bool `long:"allow-unbalance-from" description:"let the source channel go below 50% local liquidity, use if you want to drain a channel; you should also set --pfrom to >50" json:"allow_unbalance_from" toml:"allow_unbalance_from"`
AllowUnbalanceTo bool `long:"allow-unbalance-to" description:"let the target channel go above 50% local liquidity, use if you want to refill a channel; you should also set --pto to >50" json:"allow_unbalance_to" toml:"allow_unbalance_to"`
StatFilename string `short:"s" long:"stat" description:"save successful rebalance information to the specified CSV file" json:"stat" toml:"stat"`
@ -82,8 +84,14 @@ func loadConfig() {
}
if strings.Contains(cfgParams.Config, ".toml") {
_, err := toml.DecodeFile(cfgParams.Config, &params)
if err != nil {
log.Fatalf("Error opening config file %s: %s", cfgParams.Config, err)
if strings.Contains(err.Error(), "TOML value of type int64 into a Go string") {
log.Print(infoColor("Info: all prior int channel arrays are now string arrays. " +
"Make sure the following arguments in the config files are now strings:\n" +
"ExcludeChannelsIn,ExcludeChannelsOut, ExcludeChannels,ToChannel, FromChannel"))
}
log.Fatalf("Error opening config file %s: %s", cfgParams.Config, err.Error())
}
} else {
@ -92,14 +100,46 @@ func loadConfig() {
log.Fatalf("Error opening config file %s: %s", cfgParams.Config, err)
} else {
defer f.Close()
err = json.NewDecoder(f).Decode(&params)
decoder := json.NewDecoder(f)
decoder.UseNumber()
err := decoder.Decode(&params)
if err != nil {
if strings.Contains(err.Error(), "cannot unmarshal number into Go struct field") {
log.Print(infoColor("Info: all prior int channel arrays are now string arrays. " +
"Make sure the following arguments in the config files are now strings:\n" +
"ExcludeChannelsIn,ExcludeChannelsOut, ExcludeChannels,ToChannel, FromChannel"))
}
log.Fatalf("Error reading config file %s: %s", cfgParams.Config, err)
}
}
}
}
func convertChanStringToInt(chanIds []string) (channels []uint64) {
for _, cid := range chanIds {
chanId, err := strconv.ParseInt(cid, 10, 64)
if err != nil {
isScid := strings.Count(strings.ToLower(cid), "x") == 2
if isScid {
chanId = parseScid(cid)
} else {
log.Fatalf("error: parsing Channel with Id %s, %s ", cid, err)
}
}
channels = append(channels, uint64(chanId))
}
return channels
}
func tryRebalance(ctx context.Context, r *regolancer, attempt *int) (err error,
repeat bool) {
from, to, amt, err := r.pickChannelPair(params.Amount, params.MinAmount, params.RelAmountFrom, params.RelAmountTo)
@ -173,6 +213,9 @@ func main() {
if params.EconRatio == 0 && params.FeeLimitPPM == 0 {
params.EconRatio = 1
}
if params.EconRatioMaxPPM != 0 && params.FeeLimitPPM != 0 {
log.Fatalf(errColor("Error EconRatioMaxPPM and FeeLimitPPM not allowed at the same time (safety precaution)"))
}
if params.Perc > 0 {
params.FromPerc = params.Perc
params.ToPerc = params.Perc
@ -213,20 +256,24 @@ func main() {
log.Fatal("Error listing own channels: ", err)
}
if len(params.FromChannel) > 0 {
r.fromChannelId = makeChanSet(params.FromChannel)
r.fromChannelId = makeChanSet(convertChanStringToInt(params.FromChannel))
}
if len(params.ToChannel) > 0 {
r.toChannelId = makeChanSet(params.ToChannel)
r.toChannelId = makeChanSet(convertChanStringToInt(params.ToChannel))
}
r.excludeIn = makeChanSet(params.ExcludeChannelsIn)
r.excludeOut = makeChanSet(params.ExcludeChannelsOut)
r.excludeBoth = makeChanSet(params.ExcludeChannels)
r.excludeIn = makeChanSet(convertChanStringToInt(params.ExcludeChannelsIn))
r.excludeOut = makeChanSet(convertChanStringToInt(params.ExcludeChannelsOut))
r.excludeBoth = makeChanSet(convertChanStringToInt(params.ExcludeChannels))
r.invoiceCache = map[int64]*lnrpc.AddInvoiceResponse{}
err = r.makeNodeList(params.ExcludeNodes)
if err != nil {
log.Fatal("Error parsing excluded node list: ", err)
}
err = r.getChannelCandidates(params.FromPerc, params.ToPerc, params.Amount)
if err != nil {
log.Fatal("Error choosing channels: ", err)
}

@ -69,6 +69,10 @@ func (r *regolancer) calcEconFeeMsat(ctx context.Context, from, to uint64, amtMs
}
feeMsat = int64(float64(policyTo.FeeBaseMsat+amtMsat*
policyTo.FeeRateMilliMsat)*ratio/1e6) - lostProfitMsat
if params.EconRatioMaxPPM != 0 && int64(float64(feeMsat)/float64(amtMsat)*1e6) > params.EconRatioMaxPPM {
feeMsat = params.EconRatioMaxPPM * amtMsat / 1e6
}
if feeMsat < 0 {
return 0, "", fmt.Errorf("max fee less than zero")
}

Loading…
Cancel
Save