Skip to content

Commit aecb463

Browse files
authored
Merge pull request #71 from Firstset/69/invalid-denom-logging
fix coin conversion for short denom names such as om
2 parents 5671440 + 2315621 commit aecb463

16 files changed

Lines changed: 235 additions & 175 deletions

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
module github.com/firstset/tenderduty/v2
22

3-
go 1.18
3+
go 1.21
44

55
require (
66
github.com/PagerDuty/go-pagerduty v1.5.1
7+
github.com/cosmos/btcutil v1.0.4
78
github.com/cosmos/cosmos-sdk v0.45.11
89
github.com/go-passwd/validator v0.0.0-20180902184246-0b4c967e436b
910
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
1011
github.com/go-yaml/yaml v2.1.0+incompatible
1112
github.com/gorilla/websocket v1.5.0
13+
github.com/near/borsh-go v0.3.1
1214
github.com/prometheus/client_golang v1.12.2
1315
github.com/tendermint/tendermint v0.34.24
1416
github.com/textileio/go-threads v1.1.5
15-
github.com/near/borsh-go v0.3.1
1617
golang.org/x/crypto v0.1.0
1718
golang.org/x/term v0.1.0
1819
)
@@ -29,7 +30,6 @@ require (
2930
github.com/cespare/xxhash v1.1.0 // indirect
3031
github.com/cespare/xxhash/v2 v2.1.2 // indirect
3132
github.com/confio/ics23/go v0.7.0 // indirect
32-
github.com/cosmos/btcutil v1.0.4 // indirect
3333
github.com/cosmos/go-bip39 v1.0.0 // indirect
3434
github.com/cosmos/iavl v0.19.4 // indirect
3535
github.com/cosmos/ledger-cosmos-go v0.11.1 // indirect

go.sum

Lines changed: 26 additions & 13 deletions
Large diffs are not rendered by default.

main.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
_ "embed"
55
"flag"
66
"fmt"
7-
"log"
7+
"log/slog"
88
"os"
99
"syscall"
1010

@@ -48,7 +48,8 @@ func main() {
4848
fmt.Print("Please enter the encryption password: ")
4949
pass, err := term.ReadPassword(int(syscall.Stdin))
5050
if err != nil {
51-
log.Fatal(err)
51+
slog.Error("failed to read password", "err", err)
52+
os.Exit(1)
5253
}
5354
fmt.Println("")
5455
password = string(pass)
@@ -62,13 +63,14 @@ func main() {
6263
e = td2.EncryptedConfig(configFile, encryptedFile, password, true)
6364
}
6465
if e != nil {
65-
log.Fatalln(e)
66+
slog.Error("failed to process encrypted config", "err", e)
67+
os.Exit(1)
6668
}
6769
os.Exit(0)
6870
}
6971

7072
err := td2.Run(configFile, stateFile, chainConfigDirectory, &password, devMode)
7173
if err != nil {
72-
log.Println(err.Error(), "... exiting.")
74+
slog.Error("tenderduty exiting", "err", err)
7375
}
7476
}

td2/alert.go

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"context"
66
"encoding/json"
77
"fmt"
8-
"log"
8+
"log/slog"
99
"math"
1010
"net/http"
1111
"slices"
@@ -184,7 +184,7 @@ func shouldNotify(msg *alertMsg, dest notifyDest) bool {
184184
if strings.HasPrefix(msg.uniqueId, "UnvotedGovernanceProposal") {
185185
// Check if it has been 6 hours since the last (re-)send
186186
if whichMap[msg.uniqueId].SentTime.Before(time.Now().Add(-1 * time.Duration(td.GovernanceAlertsReminderInterval) * time.Hour)) {
187-
l(fmt.Sprintf("🔄 RE-SENDING ALERT on %s (%s) - notifying %s", msg.chain, msg.message, service))
187+
l(slog.LevelInfo, fmt.Sprintf("🔄 RE-SENDING ALERT on %s (%s) - notifying %s", msg.chain, msg.message, service))
188188
cache := alertMsgCache{
189189
Message: msg.message,
190190
SentTime: time.Now(),
@@ -197,11 +197,11 @@ func shouldNotify(msg *alertMsg, dest notifyDest) bool {
197197
case !whichMap[msg.uniqueId].SentTime.IsZero() && msg.resolved:
198198
// alarm is cleared
199199
delete(whichMap, msg.uniqueId)
200-
l(fmt.Sprintf("💜 Resolved alarm on %s (%s) - notifying %s", msg.chain, msg.message, service))
200+
l(slog.LevelInfo, fmt.Sprintf("💜 Resolved alarm on %s (%s) - notifying %s", msg.chain, msg.message, service))
201201
return true
202202
case msg.resolved:
203203
// it looks like we got a duplicate resolution or suppressed it. Note it and move on:
204-
l(fmt.Sprintf("😕 Not clearing alarm on %s (%s) - no corresponding alert %s", msg.chain, msg.message, service))
204+
l(slog.LevelWarn, fmt.Sprintf("😕 Not clearing alarm on %s (%s) - no corresponding alert %s", msg.chain, msg.message, service))
205205
return false
206206
}
207207

@@ -212,7 +212,7 @@ func shouldNotify(msg *alertMsg, dest notifyDest) bool {
212212

213213
// for pagerduty we perform some basic flap detection
214214
if dest == pd && msg.pd && alarms.flappingAlarms[msg.chain][msg.uniqueId].SentTime.After(time.Now().Add(-5*time.Minute)) {
215-
l("🛑 flapping detected - suppressing pagerduty notification:", msg.chain, msg.message)
215+
l(slog.LevelWarn, "🛑 flapping detected - suppressing pagerduty notification:", msg.chain, msg.message)
216216
return false
217217
} else if dest == pd && msg.pd {
218218
cache := alertMsgCache{
@@ -222,7 +222,7 @@ func shouldNotify(msg *alertMsg, dest notifyDest) bool {
222222
alarms.flappingAlarms[msg.chain][msg.uniqueId] = cache
223223
}
224224

225-
l(fmt.Sprintf("🚨 ALERT new alarm on %s (%s) - notifying %s", msg.chain, msg.message, service))
225+
l(slog.LevelInfo, fmt.Sprintf("🚨 ALERT new alarm on %s (%s) - notifying %s", msg.chain, msg.message, service))
226226
cache := alertMsgCache{
227227
Message: msg.message,
228228
SentTime: time.Now(),
@@ -301,27 +301,27 @@ func notifyDiscord(msg *alertMsg) (err error) {
301301
client := &http.Client{}
302302
data, err := json.MarshalIndent(discPost, "", " ")
303303
if err != nil {
304-
l("⚠️ Could not notify discord!", err)
304+
l(slog.LevelWarn, "⚠️ Could not notify discord!", err)
305305
return err
306306
}
307307

308308
req, err := http.NewRequest("POST", msg.discHook, bytes.NewBuffer(data))
309309
if err != nil {
310-
l("⚠️ Could not notify discord!", err)
310+
l(slog.LevelWarn, "⚠️ Could not notify discord!", err)
311311
return err
312312
}
313313
req.Header.Set("Content-Type", "application/json")
314314

315315
resp, err := client.Do(req)
316316
if err != nil {
317-
l("⚠️ Could not notify discord!", err)
317+
l(slog.LevelWarn, "⚠️ Could not notify discord!", err)
318318
return err
319319
}
320320
_ = resp.Body.Close()
321321

322322
if resp.StatusCode != 204 {
323-
log.Println(resp)
324-
l("⚠️ Could not notify discord! Returned", resp.StatusCode)
323+
slog.Warn("discord webhook returned non-success response", "status", resp.StatusCode)
324+
l(slog.LevelWarn, "⚠️ Could not notify discord! Returned", resp.StatusCode)
325325
return err
326326
}
327327
return nil
@@ -364,7 +364,7 @@ func notifyTg(msg *alertMsg) (err error) {
364364
}
365365
bot, err := tgbotapi.NewBotAPI(msg.tgKey)
366366
if err != nil {
367-
l("notify telegram:", err)
367+
l(slog.LevelWarn, "notify telegram:", err)
368368
return
369369
}
370370

@@ -376,7 +376,7 @@ func notifyTg(msg *alertMsg) (err error) {
376376
mc := tgbotapi.NewMessageToChannel(msg.tgChannel, fmt.Sprintf("%s: %s: %s", msg.chain, prefix, msg.message))
377377
_, err = bot.Send(mc)
378378
if err != nil {
379-
l("telegram send:", err)
379+
l(slog.LevelWarn, "telegram send:", err)
380380
}
381381
return err
382382
}
@@ -390,7 +390,7 @@ func notifyPagerduty(msg *alertMsg) (err error) {
390390
}
391391
// key from the example, don't spam their api
392392
if msg.key == "aaaaaaaaaaaabbbbbbbbbbbbbcccccccccccc" {
393-
l("invalid pagerduty key")
393+
l(slog.LevelWarn, "invalid pagerduty key")
394394
return
395395
}
396396
action := "trigger"
@@ -485,27 +485,27 @@ func notifyWebhook(msg *alertMsg) (err error) {
485485

486486
data, err := json.Marshal(payload)
487487
if err != nil {
488-
l("⚠️ Could not marshal webhook payload!", err)
488+
l(slog.LevelWarn, "⚠️ Could not marshal webhook payload!", err)
489489
return err
490490
}
491491

492492
req, err := http.NewRequest("POST", msg.whURL, bytes.NewBuffer(data))
493493
if err != nil {
494-
l("⚠️ Could not create webhook request!", err)
494+
l(slog.LevelWarn, "⚠️ Could not create webhook request!", err)
495495
return err
496496
}
497497
req.Header.Set("Content-Type", "application/json")
498498

499499
client := &http.Client{Timeout: 30 * time.Second}
500500
resp, err := client.Do(req)
501501
if err != nil {
502-
l("⚠️ Could not send webhook!", err)
502+
l(slog.LevelWarn, "⚠️ Could not send webhook!", err)
503503
return err
504504
}
505505
_ = resp.Body.Close()
506506

507507
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
508-
l("⚠️ Webhook returned non-success status:", resp.StatusCode)
508+
l(slog.LevelWarn, "⚠️ Webhook returned non-success status:", resp.StatusCode)
509509
return fmt.Errorf("webhook returned status %d for %s", resp.StatusCode, msg.chain)
510510
}
511511

@@ -660,7 +660,7 @@ func evaluateNoRPCEndpointsAlert(cc *ChainConfig, noNodesSec *int) (bool, bool)
660660
*noNodesSec += 2
661661
if *noNodesSec <= 60*td.NodeDownMin {
662662
if *noNodesSec%20 == 0 {
663-
l(fmt.Sprintf("no nodes available on %s for %d seconds, deferring alarm", cc.ChainId, *noNodesSec))
663+
l(slog.LevelInfo, fmt.Sprintf("no nodes available on %s for %d seconds, deferring alarm", cc.ChainId, *noNodesSec))
664664
}
665665
} else {
666666
if !alarms.exist(cc.name, alertID) {
@@ -1006,12 +1006,14 @@ func evaluateUnclaimedRewardsAlert(cc *ChainConfig) (bool, bool) {
10061006
return alert, resolved
10071007
}
10081008

1009+
convertedToDisplay := false
10091010
if cc.denomMetadata != nil {
10101011
convertedCoins, err := utils.ConvertDecCoinToDisplayUnit(nativeCoins, *cc.denomMetadata)
10111012
if err == nil {
10121013
nativeCoins = *convertedCoins
1014+
convertedToDisplay = true
10131015
} else {
1014-
l(fmt.Errorf("cannot convert rewards/commission to display unit for %s, err: %w", cc.name, err))
1016+
l(slog.LevelDebug, fmt.Errorf("cannot convert rewards/commission to display unit for %s, err: %w", cc.name, err))
10151017
}
10161018
}
10171019

@@ -1024,26 +1026,17 @@ func evaluateUnclaimedRewardsAlert(cc *ChainConfig) (bool, bool) {
10241026
return alert, resolved
10251027
}
10261028

1027-
if err := github_com_cosmos_cosmos_sdk_types.ValidateDenom(targetDenom); err != nil {
1028-
fallback := ""
1029-
if len(nativeCoins) > 0 {
1030-
if errFallback := github_com_cosmos_cosmos_sdk_types.ValidateDenom(nativeCoins[0].Denom); errFallback == nil {
1031-
fallback = nativeCoins[0].Denom
1032-
}
1033-
}
1034-
if fallback == "" {
1035-
l(fmt.Errorf("invalid target denom %q for %s: %w", targetDenom, cc.name, err))
1036-
return alert, resolved
1037-
}
1038-
l(fmt.Errorf("invalid target denom %q for %s: %w; falling back to %q", targetDenom, cc.name, err, fallback))
1039-
targetDenom = fallback
1029+
// coinPrice is for the chain display token. If metadata exists but conversion to display
1030+
// failed, the amount may still be in base units (e.g. uom), which would misprice by 10^exp.
1031+
// Skip this alert evaluation rather than raising a false amount-based alert.
1032+
if cc.denomMetadata != nil && !convertedToDisplay {
1033+
l(slog.LevelDebug, fmt.Errorf("skipping unclaimed rewards pricing for %s because rewards are not confirmed in display units", cc.name))
1034+
return alert, resolved
10401035
}
10411036

1042-
totalRewards := github_com_cosmos_cosmos_sdk_types.NewDecCoinFromDec(targetDenom, totalAmount)
1043-
10441037
coinPrice, err := td.coinMarketCapClient.GetPrice(td.ctx, cc.Slug)
1045-
if err == nil {
1046-
totalRewardsConverted := totalRewards.Amount.MustFloat64() * coinPrice.Price
1038+
if err == nil && convertedToDisplay {
1039+
totalRewardsConverted := totalAmount.MustFloat64() * coinPrice.Price
10471040
threshold := floatVal(cc.Alerts.UnclaimedRewardsThreshold)
10481041

10491042
alertID := fmt.Sprintf("UnclaimedRewards_%s", cc.ValAddress)

td2/dashboard/server.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import (
44
"embed"
55
"encoding/json"
66
"io/fs"
7-
"log"
7+
"log/slog"
88
"net/http"
9+
"os"
910
"regexp"
1011
"sort"
1112
"sync"
@@ -27,7 +28,8 @@ func Serve(port string, updates chan *ChainStatus, logs chan LogMessage, hideLog
2728
var err error
2829
rootDir, err = fs.Sub(Content, "static")
2930
if err != nil {
30-
log.Fatalln(err)
31+
slog.Error("failed to load embedded static content", "err", err)
32+
os.Exit(1)
3133
}
3234
var cast broadcast.Broadcaster
3335

@@ -150,7 +152,8 @@ func Serve(port string, updates chan *ChainStatus, logs chan LogMessage, hideLog
150152
}
151153
err = server.ListenAndServe()
152154
cast.Discard()
153-
log.Fatal("tenderduty dashboard server failed", err)
155+
slog.Error("tenderduty dashboard server failed", "err", err)
156+
os.Exit(1)
154157
}
155158

156159
// CacheHandler implements the Handler interface with a Cache-Control set on responses

td2/encryption.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"errors"
1212
"fmt"
1313
"io"
14-
"log"
14+
"log/slog"
1515
"os"
1616

1717
"github.com/go-passwd/validator"
@@ -227,6 +227,6 @@ func EncryptedConfig(plaintext, ciphertext, pass string, decrypting bool) error
227227
if decrypting {
228228
fileType = "decrypted"
229229
}
230-
log.Printf("wrote %d bytes to %s file %s\n", size, fileType, outfile)
230+
slog.Info("wrote file", "bytes", size, "type", fileType, "file", outfile)
231231
return nil
232232
}

0 commit comments

Comments
 (0)