mirror of
https://github.com/edouardparis/lntop
synced 2024-11-08 01:10:32 +00:00
Merge pull request #101 from hieblmi/forwarding-history
feature: A new forwarding history tab
This commit is contained in:
commit
c6748d321f
16
README.md
16
README.md
@ -127,6 +127,22 @@ columns = [
|
||||
"LAST UPDATE", # last update
|
||||
"DETAIL", # error description
|
||||
]
|
||||
|
||||
[views.fwdinghist]
|
||||
columns = [
|
||||
"ALIAS_IN", # peer alias name of the incoming peer
|
||||
"ALIAS_OUT", # peer alias name of the outgoing peer
|
||||
"AMT_IN", # amount of sats received
|
||||
"AMT_OUT", # amount of sats forwarded
|
||||
"FEE", # earned fee
|
||||
"TIMESTAMP_NS",# forwarding event timestamp
|
||||
# "CHAN_ID_IN", # channel id of the incomming channel
|
||||
# "CHAN_ID_OUT", # channel id of the outgoing channel
|
||||
]
|
||||
|
||||
[views.fwdinghist.options]
|
||||
START_TIME = { start_time = "-6h" }
|
||||
MAX_NUM_EVENTS = { max_num_events = "333" }
|
||||
```
|
||||
|
||||
## Routing view
|
||||
|
@ -40,6 +40,7 @@ type Views struct {
|
||||
Channels *View `toml:"channels"`
|
||||
Transactions *View `toml:"transactions"`
|
||||
Routing *View `toml:"routing"`
|
||||
FwdingHist *View `toml:"fwdinghist"`
|
||||
}
|
||||
|
||||
type ColumnOptions map[string]map[string]string
|
||||
|
@ -57,6 +57,14 @@ columns = [
|
||||
|
||||
# AGE = { color = "color" }
|
||||
|
||||
[views.fwdinghist.options]
|
||||
# The forwarding history options determine how many forwarding events the
|
||||
# forwarding history tab is displaying. The higher the number of fetched
|
||||
# forwarding events is the higher the alias lookup time, so only increase these
|
||||
# values if you can tolerate the longer loading times.
|
||||
START_TIME = { start_time = "-12h" }
|
||||
MAX_NUM_EVENTS = { max_num_events = "333" }
|
||||
|
||||
[views.transactions]
|
||||
# It is possible to add, remove and order columns of the
|
||||
# table with the array columns. The available values are:
|
||||
|
2
go.mod
2
go.mod
@ -7,7 +7,7 @@ require (
|
||||
github.com/awesome-gocui/gocui v1.1.0
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/gookit/color v1.5.2
|
||||
github.com/lightningnetwork/lnd v0.15.0-beta
|
||||
github.com/lightningnetwork/lnd v0.15.4-beta
|
||||
github.com/mattn/go-runewidth v0.0.13
|
||||
github.com/pkg/errors v0.9.1
|
||||
go.uber.org/zap v1.17.0
|
||||
|
33
go.sum
33
go.sum
@ -75,33 +75,39 @@ github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879/go.mod h1:os
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08=
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20220316175102-8d5c75c28923/go.mod h1:taIcYprAW2g6Z9S0gGUxyR+zDwimyDMK5ePOX+iJ2ds=
|
||||
github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
|
||||
github.com/btcsuite/btcd v0.23.1 h1:IB8cVQcC2X5mHbnfirLG5IZnkWYNTPlLZVrxUYSotbE=
|
||||
github.com/btcsuite/btcd v0.23.1/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
|
||||
github.com/btcsuite/btcd v0.23.3 h1:4KH/JKy9WiCd+iUS9Mu0Zp7Dnj17TGdKrg9xc/FGj24=
|
||||
github.com/btcsuite/btcd v0.23.3/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.1 h1:xP60mv8fvp+0khmrN0zTdPC3cNm24rfeE6lh2R/Yv3E=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.1/go.mod h1:9/CSmJxmuvqzX9Wh2fXMWToLOHhPd11lSPuIupwTkI8=
|
||||
github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.1 h1:hDcDaXiP0uEzR8Biqo2weECKqEw0uHDZ9ixIWevVQqY=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.1/go.mod h1:nbKlBMNm9FGsdvKvu0essceubPiAcI57pYBNnsLAa34=
|
||||
github.com/btcsuite/btcd/btcutil/psbt v1.1.4 h1:Edx4AfBn+YPam2KP5AobDitulGp4r1Oibm8oruzkMdI=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.2/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0=
|
||||
github.com/btcsuite/btcd/btcutil/psbt v1.1.4/go.mod h1:9AyU6EQVJ9Iw9zPyNT1lcdHd6cnEZdno5wLu5FY74os=
|
||||
github.com/btcsuite/btcd/btcutil/psbt v1.1.5 h1:x0ZRrYY8j75ThV6xBz86CkYAG82F5bzay4H5D1c8b/U=
|
||||
github.com/btcsuite/btcd/btcutil/psbt v1.1.5/go.mod h1:kA6FLH/JfUx++j9pYU0pyu+Z8XGBQuuTmuKYUf6q7/U=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcwallet v0.15.1 h1:SKfh/l2Bgz9sJwHZvfiVbZ8Pl3N/8fFcWWXzsAPz9GU=
|
||||
github.com/btcsuite/btcwallet v0.15.1/go.mod h1:7OFsQ8ypiRwmr67hE0z98uXgJgXGAihE79jCib9x6ag=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.2.3 h1:M2yr5UlULvpqtxUqpMxTME/pA92Z9cpqeyvAFk9lAg0=
|
||||
github.com/btcsuite/btcwallet v0.16.1 h1:nD8qXJeAU8c7a0Jlx5jwI2ufbf/9ouy29XGapRLTmos=
|
||||
github.com/btcsuite/btcwallet v0.16.1/go.mod h1:NCO8+5rIcbUm5CtVNSQM0xrtK4iYprlyuvpGzhkejaM=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.2.3/go.mod h1:T2xSiKGpUkSLCh68aF+FMXmKK9mFqNdHl9VaqOr+JjU=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 h1:etuLgGEojecsDOYTII8rYiGHjGyV5xTqsXi+ZQ715UU=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2/go.mod h1:Zpk/LOb2sKqwP2lmHjaZT9AdaKsHPSbNLm2Uql5IQ/0=
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 h1:BtEN5Empw62/RVnZ0VcJaVtVlBijnLlJY+dwjAye2Bg=
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.2.0/go.mod h1:AtkqiL7ccKWxuLYtZm8Bu8G6q82w4yIZdgq6riy60z0=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0 h1:wZnOolEAeNOHzHTnznw/wQv+j35ftCIokNrnOTOU5o8=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.2.2/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 h1:PszOub7iXVYbtGybym5TGCp9Dv1h1iX4rIC3HICZGLg=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448=
|
||||
github.com/btcsuite/btcwallet/walletdb v1.3.5/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU=
|
||||
github.com/btcsuite/btcwallet/walletdb v1.3.6-0.20210803004036-eebed51155ec/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU=
|
||||
github.com/btcsuite/btcwallet/walletdb v1.4.0 h1:/C5JRF+dTuE2CNMCO/or5N8epsrhmSM4710uBQoYPTQ=
|
||||
@ -476,8 +482,8 @@ github.com/lightninglabs/neutrino v0.14.2/go.mod h1:OICUeTCn+4Tu27YRJIpWvvqySxx4
|
||||
github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display/go.mod h1:2oKOBU042GKFHrdbgGiKax4xVrFiZu51lhacUZQ9MnE=
|
||||
github.com/lightningnetwork/lightning-onion v1.0.2-0.20220211021909-bb84a1ccb0c5 h1:TkKwqFcQTGYoI+VEqyxA8rxpCin8qDaYX0AfVRinT3k=
|
||||
github.com/lightningnetwork/lightning-onion v1.0.2-0.20220211021909-bb84a1ccb0c5/go.mod h1:7dDx73ApjEZA0kcknI799m2O5kkpfg4/gr7N092ojNo=
|
||||
github.com/lightningnetwork/lnd v0.15.0-beta h1:smzYjJqL4nGuj4qrAWdikrPzPJ8fcPRFHQ86S2tHR1M=
|
||||
github.com/lightningnetwork/lnd v0.15.0-beta/go.mod h1:Tm7LZrYeR2JQH1gEOKmd0NTCgjJ1Bnujkx4lcz9b5+A=
|
||||
github.com/lightningnetwork/lnd v0.15.4-beta h1:vO+UZjuA8RqJdDlfwQeS0h2PCocYwwqv5HkX2IXf5/M=
|
||||
github.com/lightningnetwork/lnd v0.15.4-beta/go.mod h1:6aoOkifcI9tuk8UV5l2rVZSq0681obuP4zvfK+2ZrT0=
|
||||
github.com/lightningnetwork/lnd/cert v1.1.1/go.mod h1:1P46svkkd73oSoeI4zjkVKgZNwGq8bkGuPR8z+5vQUs=
|
||||
github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg=
|
||||
github.com/lightningnetwork/lnd/clock v1.1.0 h1:/yfVAwtPmdx45aQBoXQImeY7sOIEr7IXlImRMBOZ7GQ=
|
||||
@ -663,7 +669,7 @@ github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4A
|
||||
github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
|
||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
@ -772,7 +778,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@ -798,7 +803,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/net v0.0.0-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -1001,7 +1005,6 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -35,6 +35,10 @@ func Int64(k string, i int64) Field {
|
||||
return zap.Int64(k, i)
|
||||
}
|
||||
|
||||
func Uint64(k string, i uint64) Field {
|
||||
return zap.Uint64(k, i)
|
||||
}
|
||||
|
||||
func Error(v error) Field {
|
||||
return zap.Error(v)
|
||||
}
|
||||
|
@ -43,4 +43,6 @@ type Backend interface {
|
||||
SubscribeRoutingEvents(context.Context, chan *models.RoutingEvent) error
|
||||
|
||||
SubscribeGraphEvents(context.Context, chan *models.ChannelEdgeUpdate) error
|
||||
|
||||
GetForwardingHistory(context.Context, string, uint32) ([]*models.ForwardingEvent, error)
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
@ -447,6 +449,93 @@ func (l Backend) GetNode(ctx context.Context, pubkey string, includeChannels boo
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (l Backend) GetForwardingHistory(ctx context.Context, startTime string, maxNumEvents uint32) ([]*models.ForwardingEvent, error) {
|
||||
l.logger.Debug("GetForwardingHistory")
|
||||
|
||||
clt, err := l.Client(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer clt.Close()
|
||||
t, err := parseTime(startTime, time.Now())
|
||||
req := &lnrpc.ForwardingHistoryRequest{
|
||||
StartTime: t,
|
||||
NumMaxEvents: maxNumEvents,
|
||||
}
|
||||
resp, err := clt.ForwardingHistory(ctx, req)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
result := protoToForwardingHistory(resp)
|
||||
|
||||
// Enrich peer alias names.
|
||||
// This can be removed once the ForwardingHistory
|
||||
// contains the peer aliases by default.
|
||||
enrichPeerAliases := func(ctx context.Context, events []*models.ForwardingEvent) error {
|
||||
|
||||
if len(events) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
selfInfo, err := clt.GetInfo(ctx, &lnrpc.GetInfoRequest{})
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
getPeerAlias := func(chanId uint64) (string, error) {
|
||||
chanInfo, err := clt.GetChanInfo(ctx, &lnrpc.ChanInfoRequest{
|
||||
ChanId: chanId,
|
||||
})
|
||||
if err != nil {
|
||||
return "", errors.WithStack(err)
|
||||
}
|
||||
pubKey := chanInfo.Node1Pub
|
||||
if selfInfo.IdentityPubkey == chanInfo.Node1Pub {
|
||||
pubKey = chanInfo.Node2Pub
|
||||
}
|
||||
nodeInfo, err := clt.GetNodeInfo(ctx, &lnrpc.NodeInfoRequest{
|
||||
PubKey: pubKey,
|
||||
})
|
||||
if err != nil {
|
||||
return "", errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nodeInfo.Node.Alias, nil
|
||||
}
|
||||
|
||||
cache := make(map[uint64]string)
|
||||
for i, event := range events {
|
||||
|
||||
if val, ok := cache[event.ChanIdIn]; ok {
|
||||
events[i].PeerAliasIn = val
|
||||
} else {
|
||||
events[i].PeerAliasIn, err = getPeerAlias(event.ChanIdIn)
|
||||
if err != nil {
|
||||
cache[event.ChanIdIn] = events[i].PeerAliasIn
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := cache[event.ChanIdOut]; ok {
|
||||
events[i].PeerAliasOut = val
|
||||
} else {
|
||||
events[i].PeerAliasOut, err = getPeerAlias(event.ChanIdOut)
|
||||
if err != nil {
|
||||
cache[event.ChanIdOut] = events[i].PeerAliasOut
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
err = enrichPeerAliases(ctx, result)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (l Backend) CreateInvoice(ctx context.Context, amount int64, desc string) (*models.Invoice, error) {
|
||||
l.logger.Debug("Create invoice...",
|
||||
logging.Int64("amount", amount),
|
||||
@ -563,3 +652,36 @@ func New(c *config.Network, logger logging.Logger) (*Backend, error) {
|
||||
|
||||
return backend, nil
|
||||
}
|
||||
|
||||
// reTimeRange matches systemd.time-like short negative timeranges, e.g. "-200s".
|
||||
var reTimeRange = regexp.MustCompile(`^-\d{1,18}[s|m|h|d|w|M|y]$`)
|
||||
|
||||
// secondsPer allows translating s(seconds), m(minutes), h(ours), d(ays),
|
||||
// w(eeks), M(onths) and y(ears) into corresponding seconds.
|
||||
var secondsPer = map[string]int64{
|
||||
"s": 1,
|
||||
"m": 60,
|
||||
"h": 3600,
|
||||
"d": 86400,
|
||||
"w": 604800,
|
||||
"M": 2630016, // 30.44 days
|
||||
"y": 31557600, // 365.25 days
|
||||
}
|
||||
|
||||
// parseTime parses UNIX timestamps or short timeranges inspired by systemd
|
||||
// (when starting with "-"), e.g. "-1M" for one month (30.44 days) ago.
|
||||
func parseTime(s string, base time.Time) (uint64, error) {
|
||||
if reTimeRange.MatchString(s) {
|
||||
last := len(s) - 1
|
||||
|
||||
d, err := strconv.ParseInt(s[1:last], 10, 64)
|
||||
if err != nil {
|
||||
return uint64(0), err
|
||||
}
|
||||
|
||||
mul := secondsPer[string(s[last])]
|
||||
return uint64(base.Unix() - d*mul), nil
|
||||
}
|
||||
|
||||
return strconv.ParseUint(s, 10, 64)
|
||||
}
|
||||
|
@ -398,3 +398,30 @@ func protoToRoutingEvent(resp *routerrpc.HtlcEvent) *models.RoutingEvent {
|
||||
FailureDetail: detail,
|
||||
}
|
||||
}
|
||||
|
||||
func protoToForwardingHistory(resp *lnrpc.ForwardingHistoryResponse) []*models.ForwardingEvent {
|
||||
if resp == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
forwardingEvents := make([]*models.ForwardingEvent, len(resp.ForwardingEvents))
|
||||
for i := range resp.ForwardingEvents {
|
||||
forwardingEvents[i] = protoToForwardingEvent(resp.ForwardingEvents[i])
|
||||
}
|
||||
return forwardingEvents
|
||||
}
|
||||
|
||||
func protoToForwardingEvent(resp *lnrpc.ForwardingEvent) *models.ForwardingEvent {
|
||||
return &models.ForwardingEvent{
|
||||
|
||||
ChanIdIn: resp.ChanIdIn,
|
||||
ChanIdOut: resp.ChanIdOut,
|
||||
AmtIn: resp.AmtIn,
|
||||
AmtOut: resp.AmtOut,
|
||||
Fee: resp.Fee,
|
||||
FeeMsat: resp.FeeMsat,
|
||||
AmtInMsat: resp.AmtInMsat,
|
||||
AmtOutMsat: resp.AmtOutMsat,
|
||||
EventTime: time.Unix(0, int64(resp.TimestampNs)),
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +86,10 @@ func (b *Backend) DecodePayReq(ctx context.Context, payreq string) (*models.PayR
|
||||
return &models.PayReq{}, nil
|
||||
}
|
||||
|
||||
func (b *Backend) GetForwardingHistory(ctx context.Context, startTime string, maxNumEvents uint32) ([]*models.ForwardingEvent, error) {
|
||||
return []*models.ForwardingEvent{}, nil
|
||||
}
|
||||
|
||||
func (b *Backend) CreateInvoice(ctx context.Context, amt int64, desc string) (*models.Invoice, error) {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
17
network/models/fwdingevent.go
Normal file
17
network/models/fwdingevent.go
Normal file
@ -0,0 +1,17 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
type ForwardingEvent struct {
|
||||
PeerAliasIn string
|
||||
PeerAliasOut string
|
||||
ChanIdIn uint64
|
||||
ChanIdOut uint64
|
||||
AmtIn uint64
|
||||
AmtOut uint64
|
||||
Fee uint64
|
||||
FeeMsat uint64
|
||||
AmtInMsat uint64
|
||||
AmtOutMsat uint64
|
||||
EventTime time.Time
|
||||
}
|
@ -227,6 +227,8 @@ func (c *controller) Order(order models.Order) func(*gocui.Gui, *gocui.View) err
|
||||
c.views.Channels.Sort("", order)
|
||||
case views.TRANSACTIONS:
|
||||
c.views.Transactions.Sort("", order)
|
||||
case views.FWDINGHIST:
|
||||
c.views.FwdingHist.Sort("", order)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -293,6 +295,19 @@ func (c *controller) OnEnter(g *gocui.Gui, v *gocui.View) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case views.FWDINGHIST:
|
||||
err := c.views.Main.Delete(g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*60)
|
||||
defer cancel()
|
||||
c.models.RefreshForwardingHistory(ctx)
|
||||
c.views.Main = c.views.FwdingHist
|
||||
err = c.views.FwdingHist.Set(g, 11, 6, maxX-1, maxY)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case views.TRANSACTIONS:
|
||||
|
72
ui/models/fwdinghist.go
Normal file
72
ui/models/fwdinghist.go
Normal file
@ -0,0 +1,72 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/edouardparis/lntop/network/models"
|
||||
)
|
||||
|
||||
type FwdinghistSort func(*models.ForwardingEvent, *models.ForwardingEvent) bool
|
||||
|
||||
type FwdingHist struct {
|
||||
StartTime string
|
||||
MaxNumEvents uint32
|
||||
current *models.ForwardingEvent
|
||||
list []*models.ForwardingEvent
|
||||
sort FwdinghistSort
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func (t *FwdingHist) Current() *models.ForwardingEvent {
|
||||
return t.current
|
||||
}
|
||||
|
||||
func (t *FwdingHist) SetCurrent(index int) {
|
||||
t.current = t.Get(index)
|
||||
}
|
||||
|
||||
func (t *FwdingHist) List() []*models.ForwardingEvent {
|
||||
return t.list
|
||||
}
|
||||
|
||||
func (t *FwdingHist) Len() int {
|
||||
return len(t.list)
|
||||
}
|
||||
|
||||
func (t *FwdingHist) Clear() {
|
||||
t.list = []*models.ForwardingEvent{}
|
||||
}
|
||||
|
||||
func (t *FwdingHist) Swap(i, j int) {
|
||||
t.list[i], t.list[j] = t.list[j], t.list[i]
|
||||
}
|
||||
|
||||
func (t *FwdingHist) Less(i, j int) bool {
|
||||
return t.sort(t.list[i], t.list[j])
|
||||
}
|
||||
|
||||
func (t *FwdingHist) Sort(s FwdinghistSort) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
t.sort = s
|
||||
sort.Sort(t)
|
||||
}
|
||||
|
||||
func (t *FwdingHist) Get(index int) *models.ForwardingEvent {
|
||||
if index < 0 || index > len(t.list)-1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return t.list[index]
|
||||
}
|
||||
|
||||
func (t *FwdingHist) Update(events []*models.ForwardingEvent) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
t.Clear()
|
||||
for _, event := range events {
|
||||
t.list = append(t.list, event)
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/edouardparis/lntop/app"
|
||||
"github.com/edouardparis/lntop/logging"
|
||||
@ -19,9 +20,27 @@ type Models struct {
|
||||
ChannelsBalance *ChannelsBalance
|
||||
Transactions *Transactions
|
||||
RoutingLog *RoutingLog
|
||||
FwdingHist *FwdingHist
|
||||
}
|
||||
|
||||
func New(app *app.App) *Models {
|
||||
fwdingHist := FwdingHist{}
|
||||
startTime := app.Config.Views.FwdingHist.Options.GetOption("START_TIME", "start_time")
|
||||
maxNumEvents := app.Config.Views.FwdingHist.Options.GetOption("MAX_NUM_EVENTS", "max_num_events")
|
||||
|
||||
if startTime != "" {
|
||||
fwdingHist.StartTime = startTime
|
||||
}
|
||||
|
||||
if maxNumEvents != "" {
|
||||
max, err := strconv.ParseUint(maxNumEvents, 10, 32)
|
||||
if err != nil {
|
||||
app.Logger.Info("Couldn't parse the maximum number of forwarding events.")
|
||||
} else {
|
||||
fwdingHist.MaxNumEvents = uint32(max)
|
||||
}
|
||||
}
|
||||
|
||||
return &Models{
|
||||
logger: app.Logger.With(logging.String("logger", "models")),
|
||||
network: app.Network,
|
||||
@ -31,6 +50,7 @@ func New(app *app.App) *Models {
|
||||
ChannelsBalance: &ChannelsBalance{},
|
||||
Transactions: &Transactions{},
|
||||
RoutingLog: &RoutingLog{},
|
||||
FwdingHist: &fwdingHist,
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,6 +67,17 @@ func (m *Models) RefreshInfo(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Models) RefreshForwardingHistory(ctx context.Context) error {
|
||||
forwardingEvents, err := m.network.GetForwardingHistory(ctx, m.FwdingHist.StartTime, m.FwdingHist.MaxNumEvents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.FwdingHist.Update(forwardingEvents)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Models) RefreshChannels(ctx context.Context) error {
|
||||
channels, err := m.network.ListChannels(ctx, options.WithChannelPending)
|
||||
if err != nil {
|
||||
|
418
ui/views/fwdinghist.go
Normal file
418
ui/views/fwdinghist.go
Normal file
@ -0,0 +1,418 @@
|
||||
package views
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
|
||||
"github.com/edouardparis/lntop/config"
|
||||
netmodels "github.com/edouardparis/lntop/network/models"
|
||||
"github.com/edouardparis/lntop/ui/color"
|
||||
"github.com/edouardparis/lntop/ui/models"
|
||||
)
|
||||
|
||||
const (
|
||||
FWDINGHIST = "fwdinghist"
|
||||
FWDINGHIST_COLUMNS = "fwdinghist_columns"
|
||||
FWDINGHIST_FOOTER = "fwdinghist_footer"
|
||||
)
|
||||
|
||||
var DefaultFwdinghistColumns = []string{
|
||||
"ALIAS_IN",
|
||||
"ALIAS_OUT",
|
||||
"AMT_IN",
|
||||
"AMT_OUT",
|
||||
"FEE",
|
||||
"TIMESTAMP_NS",
|
||||
"CHAN_ID_IN",
|
||||
"CHAN_ID_OUT",
|
||||
}
|
||||
|
||||
type FwdingHist struct {
|
||||
cfg *config.View
|
||||
|
||||
columns []fwdinghistColumn
|
||||
columnHeadersView *gocui.View
|
||||
view *gocui.View
|
||||
fwdinghist *models.FwdingHist
|
||||
|
||||
ox, oy int
|
||||
cx, cy int
|
||||
}
|
||||
|
||||
type fwdinghistColumn struct {
|
||||
name string
|
||||
width int
|
||||
sorted bool
|
||||
sort func(models.Order) models.FwdinghistSort
|
||||
display func(*netmodels.ForwardingEvent, ...color.Option) string
|
||||
}
|
||||
|
||||
func (c FwdingHist) Index() int {
|
||||
_, oy := c.view.Origin()
|
||||
_, cy := c.view.Cursor()
|
||||
return cy + oy
|
||||
}
|
||||
|
||||
func (c FwdingHist) Name() string {
|
||||
return FWDINGHIST
|
||||
}
|
||||
|
||||
func (c *FwdingHist) Wrap(v *gocui.View) View {
|
||||
c.view = v
|
||||
return c
|
||||
}
|
||||
|
||||
func (c FwdingHist) currentColumnIndex() int {
|
||||
x := c.ox + c.cx
|
||||
index := 0
|
||||
sum := 0
|
||||
for i := range c.columns {
|
||||
sum += c.columns[i].width + 1
|
||||
if x < sum {
|
||||
return index
|
||||
}
|
||||
index++
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
func (c FwdingHist) Origin() (int, int) {
|
||||
return c.ox, c.oy
|
||||
}
|
||||
|
||||
func (c FwdingHist) Cursor() (int, int) {
|
||||
return c.cx, c.cy
|
||||
}
|
||||
|
||||
func (c *FwdingHist) SetCursor(cx, cy int) error {
|
||||
if err := cursorCompat(c.columnHeadersView, cx, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
err := c.columnHeadersView.SetCursor(cx, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cursorCompat(c.view, cx, cy); err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.view.SetCursor(cx, cy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.cx, c.cy = cx, cy
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FwdingHist) SetOrigin(ox, oy int) error {
|
||||
err := c.columnHeadersView.SetOrigin(ox, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.view.SetOrigin(ox, oy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.ox, c.oy = ox, oy
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FwdingHist) Speed() (int, int, int, int) {
|
||||
current := c.currentColumnIndex()
|
||||
up := 0
|
||||
down := 0
|
||||
if c.Index() > 0 {
|
||||
up = 1
|
||||
}
|
||||
if c.Index() < c.fwdinghist.Len()-1 {
|
||||
down = 1
|
||||
}
|
||||
if current > len(c.columns)-1 {
|
||||
return 0, c.columns[current-1].width + 1, down, up
|
||||
}
|
||||
if current == 0 {
|
||||
return c.columns[0].width + 1, 0, down, up
|
||||
}
|
||||
return c.columns[current].width + 1,
|
||||
c.columns[current-1].width + 1,
|
||||
down, up
|
||||
}
|
||||
|
||||
func (c *FwdingHist) Limits() (pageSize int, fullSize int) {
|
||||
_, pageSize = c.view.Size()
|
||||
fullSize = c.fwdinghist.Len()
|
||||
return
|
||||
}
|
||||
|
||||
func (c *FwdingHist) Sort(column string, order models.Order) {
|
||||
if column == "" {
|
||||
index := c.currentColumnIndex()
|
||||
if index >= len(c.columns) {
|
||||
return
|
||||
}
|
||||
col := c.columns[index]
|
||||
if col.sort == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.fwdinghist.Sort(col.sort(order))
|
||||
for i := range c.columns {
|
||||
c.columns[i].sorted = (i == index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c FwdingHist) Delete(g *gocui.Gui) error {
|
||||
err := g.DeleteView(FWDINGHIST_COLUMNS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = g.DeleteView(FWDINGHIST)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return g.DeleteView(FWDINGHIST_FOOTER)
|
||||
}
|
||||
|
||||
func (c *FwdingHist) Set(g *gocui.Gui, x0, y0, x1, y1 int) error {
|
||||
var err error
|
||||
setCursor := false
|
||||
c.columnHeadersView, err = g.SetView(FWDINGHIST_COLUMNS, x0-1, y0, x1+2, y0+2, 0)
|
||||
if err != nil {
|
||||
if err != gocui.ErrUnknownView {
|
||||
return err
|
||||
}
|
||||
setCursor = true
|
||||
}
|
||||
c.columnHeadersView.Frame = false
|
||||
c.columnHeadersView.BgColor = gocui.ColorGreen
|
||||
c.columnHeadersView.FgColor = gocui.ColorBlack
|
||||
|
||||
c.view, err = g.SetView(FWDINGHIST, x0-1, y0+1, x1+2, y1-1, 0)
|
||||
if err != nil {
|
||||
if err != gocui.ErrUnknownView {
|
||||
return err
|
||||
}
|
||||
setCursor = true
|
||||
}
|
||||
c.view.Frame = false
|
||||
c.view.Autoscroll = false
|
||||
c.view.SelBgColor = gocui.ColorCyan
|
||||
c.view.SelFgColor = gocui.ColorBlack | gocui.AttrDim
|
||||
c.view.Highlight = true
|
||||
c.display()
|
||||
|
||||
if setCursor {
|
||||
ox, oy := c.Origin()
|
||||
err := c.SetOrigin(ox, oy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cx, cy := c.Cursor()
|
||||
err = c.SetCursor(cx, cy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
footer, err := g.SetView(FWDINGHIST_FOOTER, x0-1, y1-2, x1+2, y1, 0)
|
||||
if err != nil {
|
||||
if err != gocui.ErrUnknownView {
|
||||
return err
|
||||
}
|
||||
}
|
||||
footer.Frame = false
|
||||
footer.BgColor = gocui.ColorCyan
|
||||
footer.FgColor = gocui.ColorBlack
|
||||
footer.Rewind()
|
||||
blackBg := color.Black(color.Background)
|
||||
fmt.Fprintln(footer, fmt.Sprintf("%s%s %s%s %s%s",
|
||||
blackBg("F2"), "Menu",
|
||||
blackBg("Enter"), "FwdingHist",
|
||||
blackBg("F10"), "Quit",
|
||||
))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FwdingHist) display() {
|
||||
c.columnHeadersView.Rewind()
|
||||
var buffer bytes.Buffer
|
||||
current := c.currentColumnIndex()
|
||||
for i := range c.columns {
|
||||
if current == i {
|
||||
buffer.WriteString(color.Cyan(color.Background)(c.columns[i].name))
|
||||
buffer.WriteString(" ")
|
||||
continue
|
||||
} else if c.columns[i].sorted {
|
||||
buffer.WriteString(color.Magenta(color.Background)(c.columns[i].name))
|
||||
buffer.WriteString(" ")
|
||||
continue
|
||||
}
|
||||
buffer.WriteString(c.columns[i].name)
|
||||
buffer.WriteString(" ")
|
||||
}
|
||||
fmt.Fprintln(c.columnHeadersView, buffer.String())
|
||||
|
||||
c.view.Rewind()
|
||||
for _, item := range c.fwdinghist.List() {
|
||||
var buffer bytes.Buffer
|
||||
for i := range c.columns {
|
||||
var opt color.Option
|
||||
if current == i {
|
||||
opt = color.Bold
|
||||
}
|
||||
buffer.WriteString(c.columns[i].display(item, opt))
|
||||
buffer.WriteString(" ")
|
||||
}
|
||||
fmt.Fprintln(c.view, buffer.String())
|
||||
}
|
||||
}
|
||||
|
||||
func NewFwdingHist(cfg *config.View, hist *models.FwdingHist) *FwdingHist {
|
||||
fwdinghist := &FwdingHist{
|
||||
cfg: cfg,
|
||||
fwdinghist: hist,
|
||||
}
|
||||
|
||||
printer := message.NewPrinter(language.English)
|
||||
|
||||
columns := DefaultFwdinghistColumns
|
||||
if cfg != nil && len(cfg.Columns) != 0 {
|
||||
columns = cfg.Columns
|
||||
}
|
||||
|
||||
fwdinghist.columns = make([]fwdinghistColumn, len(columns))
|
||||
|
||||
for i := range columns {
|
||||
switch columns[i] {
|
||||
case "ALIAS_IN":
|
||||
fwdinghist.columns[i] = fwdinghistColumn{
|
||||
width: 30,
|
||||
name: fmt.Sprintf("%30s", columns[i]),
|
||||
sort: func(order models.Order) models.FwdinghistSort {
|
||||
return func(e1, e2 *netmodels.ForwardingEvent) bool {
|
||||
return models.StringSort(e1.PeerAliasIn, e2.PeerAliasOut, order)
|
||||
}
|
||||
},
|
||||
display: func(e *netmodels.ForwardingEvent, opts ...color.Option) string {
|
||||
return color.White(opts...)(fmt.Sprintf("%30s", e.PeerAliasIn))
|
||||
},
|
||||
}
|
||||
case "ALIAS_OUT":
|
||||
fwdinghist.columns[i] = fwdinghistColumn{
|
||||
width: 30,
|
||||
name: fmt.Sprintf("%30s", columns[i]),
|
||||
sort: func(order models.Order) models.FwdinghistSort {
|
||||
return func(e1, e2 *netmodels.ForwardingEvent) bool {
|
||||
return models.StringSort(e1.PeerAliasOut, e2.PeerAliasOut, order)
|
||||
}
|
||||
},
|
||||
display: func(e *netmodels.ForwardingEvent, opts ...color.Option) string {
|
||||
return color.White(opts...)(fmt.Sprintf("%30s", e.PeerAliasOut))
|
||||
},
|
||||
}
|
||||
case "CHAN_ID_IN":
|
||||
fwdinghist.columns[i] = fwdinghistColumn{
|
||||
width: 19,
|
||||
name: fmt.Sprintf("%19s", columns[i]),
|
||||
sort: func(order models.Order) models.FwdinghistSort {
|
||||
return func(e1, e2 *netmodels.ForwardingEvent) bool {
|
||||
return models.UInt64Sort(e1.ChanIdIn, e2.ChanIdIn, order)
|
||||
}
|
||||
},
|
||||
display: func(e *netmodels.ForwardingEvent, opts ...color.Option) string {
|
||||
return color.White(opts...)(fmt.Sprintf("%19d", e.ChanIdIn))
|
||||
},
|
||||
}
|
||||
case "CHAN_ID_OUT":
|
||||
fwdinghist.columns[i] = fwdinghistColumn{
|
||||
width: 19,
|
||||
name: fmt.Sprintf("%19s", columns[i]),
|
||||
sort: func(order models.Order) models.FwdinghistSort {
|
||||
return func(e1, e2 *netmodels.ForwardingEvent) bool {
|
||||
return models.UInt64Sort(e1.ChanIdOut, e2.ChanIdOut, order)
|
||||
}
|
||||
},
|
||||
display: func(e *netmodels.ForwardingEvent, opts ...color.Option) string {
|
||||
return color.White(opts...)(fmt.Sprintf("%19d", e.ChanIdOut))
|
||||
},
|
||||
}
|
||||
case "AMT_IN":
|
||||
fwdinghist.columns[i] = fwdinghistColumn{
|
||||
width: 12,
|
||||
name: fmt.Sprintf("%12s", "RECEIVED"),
|
||||
sort: func(order models.Order) models.FwdinghistSort {
|
||||
return func(e1, e2 *netmodels.ForwardingEvent) bool {
|
||||
return models.UInt64Sort(e1.AmtIn, e2.AmtIn, order)
|
||||
}
|
||||
},
|
||||
display: func(e *netmodels.ForwardingEvent, opts ...color.Option) string {
|
||||
return color.White(opts...)(printer.Sprintf("%12d", e.AmtIn))
|
||||
},
|
||||
}
|
||||
case "AMT_OUT":
|
||||
fwdinghist.columns[i] = fwdinghistColumn{
|
||||
width: 12,
|
||||
name: fmt.Sprintf("%12s", "SENT"),
|
||||
sort: func(order models.Order) models.FwdinghistSort {
|
||||
return func(e1, e2 *netmodels.ForwardingEvent) bool {
|
||||
return models.UInt64Sort(e1.AmtOut, e2.AmtOut, order)
|
||||
}
|
||||
},
|
||||
display: func(e *netmodels.ForwardingEvent, opts ...color.Option) string {
|
||||
return color.White(opts...)(printer.Sprintf("%12d", e.AmtOut))
|
||||
},
|
||||
}
|
||||
case "FEE":
|
||||
fwdinghist.columns[i] = fwdinghistColumn{
|
||||
name: fmt.Sprintf("%9s", "EARNED"),
|
||||
width: 9,
|
||||
sort: func(order models.Order) models.FwdinghistSort {
|
||||
return func(e1, e2 *netmodels.ForwardingEvent) bool {
|
||||
return models.UInt64Sort(e1.Fee, e2.Fee, order)
|
||||
}
|
||||
},
|
||||
display: func(e *netmodels.ForwardingEvent, opts ...color.Option) string {
|
||||
return fee(e.Fee)
|
||||
},
|
||||
}
|
||||
case "TIMESTAMP_NS":
|
||||
fwdinghist.columns[i] = fwdinghistColumn{
|
||||
name: fmt.Sprintf("%15s", "TIME"),
|
||||
width: 20,
|
||||
display: func(e *netmodels.ForwardingEvent, opts ...color.Option) string {
|
||||
return color.White(opts...)(fmt.Sprintf("%20s", e.EventTime.Format("15:04:05 Jan _2")))
|
||||
},
|
||||
}
|
||||
default:
|
||||
fwdinghist.columns[i] = fwdinghistColumn{
|
||||
name: fmt.Sprintf("%-21s", columns[i]),
|
||||
width: 21,
|
||||
display: func(tx *netmodels.ForwardingEvent, opts ...color.Option) string {
|
||||
return "column does not exist"
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return fwdinghist
|
||||
}
|
||||
|
||||
func fee(fee uint64, opts ...color.Option) string {
|
||||
if fee >= 0 && fee < 100 {
|
||||
return color.Cyan(opts...)(fmt.Sprintf("%9d", fee))
|
||||
} else if fee >= 100 && fee < 999 {
|
||||
return color.Green(opts...)(fmt.Sprintf("%9d", fee))
|
||||
}
|
||||
|
||||
return color.Yellow(opts...)(fmt.Sprintf("%9d", fee))
|
||||
}
|
@ -17,6 +17,7 @@ var menu = []string{
|
||||
"CHANNEL",
|
||||
"TRANSAC",
|
||||
"ROUTING",
|
||||
"FWDHIST",
|
||||
}
|
||||
|
||||
type Menu struct {
|
||||
@ -85,6 +86,8 @@ func (h Menu) Current() string {
|
||||
return TRANSACTIONS
|
||||
case "ROUTING":
|
||||
return ROUTING
|
||||
case "FWDHIST":
|
||||
return FWDINGHIST
|
||||
}
|
||||
}
|
||||
return ""
|
||||
|
@ -31,6 +31,7 @@ type Views struct {
|
||||
Transactions *Transactions
|
||||
Transaction *Transaction
|
||||
Routing *Routing
|
||||
FwdingHist *FwdingHist
|
||||
}
|
||||
|
||||
func (v Views) Get(vi *gocui.View) View {
|
||||
@ -50,6 +51,8 @@ func (v Views) Get(vi *gocui.View) View {
|
||||
return v.Transaction.Wrap(vi)
|
||||
case ROUTING:
|
||||
return v.Routing.Wrap(vi)
|
||||
case FWDINGHIST:
|
||||
return v.FwdingHist.Wrap(vi)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@ -106,6 +109,7 @@ func New(cfg config.Views, m *models.Models) *Views {
|
||||
Transactions: NewTransactions(cfg.Transactions, m.Transactions),
|
||||
Transaction: NewTransaction(m.Transactions),
|
||||
Routing: NewRouting(cfg.Routing, m.RoutingLog, m.Channels),
|
||||
FwdingHist: NewFwdingHist(cfg.FwdingHist, m.FwdingHist),
|
||||
Main: main,
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user