diff --git a/client/app/config.go b/client/app/config.go index ac79b7cc46..2a0ed06933 100644 --- a/client/app/config.go +++ b/client/app/config.go @@ -113,9 +113,11 @@ type WebConfig struct { // LogConfig encapsulates the logging-related settings. type LogConfig struct { - LogPath string `long:"logpath" description:"A file to save app logs"` - DebugLevel string `long:"log" description:"Logging level {trace, debug, info, warn, error, critical}"` - LocalLogs bool `long:"loglocal" description:"Use local time zone time stamps in log entries."` + LogPath string `long:"logpath" description:"A file to save app logs"` + DebugLevel string `long:"log" description:"Logging level {trace, debug, info, warn, error, critical}"` + LocalLogs bool `long:"loglocal" description:"Use local time zone time stamps in log entries."` + SkynetAPIKey string `long:"skynetapikey" description:"Use this API key to upload trade recovery data to Skynet service."` + SkynetAPIURL string `long:"skynetapiurl" description:"Skynet service API URL where trade recovery data will be uploaded."` } // Config is the common application configuration definition. This composite diff --git a/client/app/log.go b/client/app/log.go index e135256f05..ac057221bb 100644 --- a/client/app/log.go +++ b/client/app/log.go @@ -39,10 +39,10 @@ func (w logWriter) Write(p []byte) (n int, err error) { return w.Rotator.Write(p) } -// initLogging initializes the logging rotater to write logs to logFile and +// InitLogging initializes the logging rotater to write logs to logFile and // create roll files in the same directory. initLogging must be called before // the package-global log rotator variables are used. -func InitLogging(logFilename, lvl string, stdout bool, utc bool) (lm *dex.LoggerMaker, closeFn func()) { +func InitLogging(logFilename, lvl string, stdout bool, lmOptions ...dex.LoggerMakerOption) (lm *dex.LoggerMaker, closeFn func()) { logDirectory := filepath.Dir(logFilename) err := os.MkdirAll(logDirectory, 0700) if err != nil { @@ -57,7 +57,7 @@ func InitLogging(logFilename, lvl string, stdout bool, utc bool) (lm *dex.Logger if !stdout { fmt.Println("Logging to", logFilename) } - lm, err = dex.NewLoggerMaker(&logWriter{logRotator, stdout}, lvl, utc) + lm, err = dex.NewLoggerMaker(&logWriter{logRotator, stdout}, lvl, lmOptions...) if err != nil { fmt.Fprintf(os.Stderr, "failed to create custom logger: %v\n", err) os.Exit(1) diff --git a/client/asset/btc/btc.go b/client/asset/btc/btc.go index 16c7515587..16beac3ed3 100644 --- a/client/asset/btc/btc.go +++ b/client/asset/btc/btc.go @@ -4031,6 +4031,7 @@ func (btc *baseWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, ui txHash := btc.hashTx(msgTx) // Prepare the receipts. + var recoveryData string // contains recovery data to be logged receipts := make([]asset.Receipt, 0, swapCount) for i, contract := range swaps.Contracts { output := newOutput(txHash, uint32(i), contract.Value) @@ -4051,6 +4052,32 @@ func (btc *baseWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, ui expiration: time.Unix(int64(contract.LockTime), 0).UTC(), signedRefund: refundBuff.Bytes(), }) + rawRefund := receipts[i].SignedRefund() + if len(rawRefund) == 0 { + rawRefund = dex.Bytes("empty/absent") // so it's immediately clear we are lacking refund data + } + recoveryData = fmt.Sprintf("%scoin:%q contract:%q refundTx:%s", recoveryData, receipts[i].Coin(), receipts[i].Contract(), rawRefund) + if i != len(receipts)-1 { + recoveryData = fmt.Sprintf("%s, ", recoveryData) + } + } + + // Logging trade recovery info here which is needed for recovering funds in + // the following scenarios: + // # For Maker + // - only refund is problematic, since Maker makes the first move on redeeming + // # For Taker + // - refund is problematic (same as for Maker), and Taker MUST refund before Maker + // manages BOTH: redeem Taker's funds and refund his own funds (which is permitted + // after 20h with current protocol config) + // - redeem is problematic, if Maker already redeemed Taker will need secret + + // secretHash in order to redeem his part (he can't refund after this point) + // We could encrypt payload ... but networking metadata is still exposed, don't + // really care at the moment. + // For background context see https://github.com/decred/dcrdex/issues/952#issuecomment-1365657079. + err = btc.log.UploadRecoveryData(recoveryData) + if err != nil { + return nil, nil, 0, fmt.Errorf("Swap: couldn't upload trade recovery data to external service: %w", err) } // Refund txs prepared and signed. Can now broadcast the swap(s). diff --git a/client/asset/dcr/dcr.go b/client/asset/dcr/dcr.go index 83bd57fea0..424ab2c31c 100644 --- a/client/asset/dcr/dcr.go +++ b/client/asset/dcr/dcr.go @@ -3164,6 +3164,7 @@ func (dcr *ExchangeWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin return nil, nil, 0, err } + var recoveryData string // contains recovery data to be logged receipts := make([]asset.Receipt, 0, swapCount) txHash := msgTx.TxHash() for i, contract := range swaps.Contracts { @@ -3182,6 +3183,32 @@ func (dcr *ExchangeWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin expiration: time.Unix(int64(contract.LockTime), 0).UTC(), signedRefund: refundB, }) + rawRefund := receipts[i].SignedRefund() + if len(rawRefund) == 0 { + rawRefund = dex.Bytes("empty/absent") // so it's immediately clear we are lacking refund data + } + recoveryData = fmt.Sprintf("%scoin:%q contract:%q refundTx:%s", recoveryData, receipts[i].Coin(), receipts[i].Contract(), rawRefund) + if i != len(receipts)-1 { + recoveryData = fmt.Sprintf("%s, ", recoveryData) + } + } + + // Logging trade recovery info here which is needed for recovering funds in + // the following scenarios: + // # For Maker + // - only refund is problematic, since Maker makes the first move on redeeming + // # For Taker + // - refund is problematic (same as for Maker), and Taker MUST refund before Maker + // manages BOTH: redeem Taker's funds and refund his own funds (which is permitted + // after 20h with current protocol config) + // - redeem is problematic, if Maker already redeemed Taker will need secret + + // secretHash in order to redeem his part (he can't refund after this point) + // We could encrypt payload ... but networking metadata is still exposed, don't + // really care at the moment. + // For background context see https://github.com/decred/dcrdex/issues/952#issuecomment-1365657079. + err = dcr.log.UploadRecoveryData(recoveryData) + if err != nil { + return nil, nil, 0, fmt.Errorf("Swap: couldn't upload trade recovery data to external service: %w", err) } // Refund txs prepared and signed. Can now broadcast the swap(s). diff --git a/client/asset/eth/eth.go b/client/asset/eth/eth.go index 73e09b1108..8eede09570 100644 --- a/client/asset/eth/eth.go +++ b/client/asset/eth/eth.go @@ -33,6 +33,7 @@ import ( "decred.org/dcrdex/dex/keygen" dexeth "decred.org/dcrdex/dex/networks/eth" dexpolygon "decred.org/dcrdex/dex/networks/polygon" + "github.com/davecgh/go-spew/spew" "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" "github.com/decred/dcrd/hdkeychain/v3" "github.com/ethereum/go-ethereum" @@ -1929,28 +1930,52 @@ func (w *ETHWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uint6 } } - tx, err := w.initiate(w.ctx, w.assetID, swaps.Contracts, swaps.FeeRate, gasLimit, swaps.Version) - if err != nil { - return fail("Swap: initiate error: %w", err) - } - - txHash := tx.Hash() - w.addPendingTx(w.bipID, txHash, tx.Nonce(), swapVal, 0, fees) - - receipts := make([]asset.Receipt, 0, n) + rcpts := make([]swapReceipt, 0, n) for _, swap := range swaps.Contracts { var secretHash [dexeth.SecretHashSize]byte copy(secretHash[:], swap.SecretHash) - receipts = append(receipts, &swapReceipt{ + rcpts = append(rcpts, swapReceipt{ expiration: time.Unix(int64(swap.LockTime), 0), value: swap.Value, - txHash: txHash, secretHash: secretHash, ver: swaps.Version, contractAddr: dexeth.ContractAddresses[swaps.Version][w.net].String(), }) } + // Logging trade recovery info here which is needed for recovering funds in + // the following scenarios: + // # For Maker + // - only refund is problematic, since Maker makes the first move on redeeming + // # For Taker + // - refund is problematic (same as for Maker), and Taker MUST refund before Maker + // manages BOTH: redeem Taker's funds and refund his own funds (which is permitted + // after 20h with current protocol config) + // - redeem is problematic, if Maker already redeemed Taker will need secret + + // secretHash in order to redeem his part (he can't refund after this point) + // We could encrypt payload ... but networking metadata is still exposed, don't + // really care at the moment. + // For background context see https://github.com/decred/dcrdex/issues/952#issuecomment-1365657079. + err = w.log.UploadRecoveryData(spew.Sdump(rcpts)) + if err != nil { + return fail("Swap: couldn't upload trade recovery data to external service: %w", err) + } + + tx, err := w.initiate(w.ctx, w.assetID, swaps.Contracts, swaps.FeeRate, gasLimit, swaps.Version) + if err != nil { + return fail("Swap: initiate error: %w", err) + } + + txHash := tx.Hash() + w.addPendingTx(w.bipID, txHash, tx.Nonce(), swapVal, 0, fees) + + receipts := make([]asset.Receipt, 0, n) + for _, r := range rcpts { + receipt := r // gotta copy + receipt.txHash = txHash // is only available after txn has been published + receipts = append(receipts, &receipt) + } + var change asset.Coin if swaps.LockChange { w.unlockFunds(swapVal+fees, initiationReserve) @@ -2014,6 +2039,37 @@ func (w *TokenWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uin } // See (*ETHWallet).Swap comments for a third option. } + rcpts := make([]swapReceipt, 0, n) + for _, swap := range swaps.Contracts { + var secretHash [dexeth.SecretHashSize]byte + copy(secretHash[:], swap.SecretHash) + rcpts = append(rcpts, swapReceipt{ + expiration: time.Unix(int64(swap.LockTime), 0), + value: swap.Value, + secretHash: secretHash, + ver: swaps.Version, + contractAddr: w.netToken.SwapContracts[swaps.Version].Address.String(), + }) + } + + // Logging trade recovery info here which is needed for recovering funds in + // the following scenarios: + // # For Maker + // - only refund is problematic, since Maker makes the first move on redeeming + // # For Taker + // - refund is problematic (same as for Maker), and Taker MUST refund before Maker + // manages BOTH: redeem Taker's funds and refund his own funds (which is permitted + // after 20h with current protocol config) + // - redeem is problematic, if Maker already redeemed Taker will need secret + + // secretHash in order to redeem his part (he can't refund after this point) + // We could encrypt payload ... but networking metadata is still exposed, don't + // really care at the moment. + // For background context see https://github.com/decred/dcrdex/issues/952#issuecomment-1365657079. + err = w.log.UploadRecoveryData(spew.Sdump(rcpts)) + if err != nil { + return fail("Swap: couldn't upload trade recovery data to external service: %w", err) + } + tx, err := w.initiate(w.ctx, w.assetID, swaps.Contracts, swaps.FeeRate, gasLimit, swaps.Version) if err != nil { return fail("Swap: initiate error: %w", err) @@ -2023,23 +2079,14 @@ func (w *TokenWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uin return fail("unable to find contract address for asset %d contract version %d", w.assetID, swaps.Version) } - contractAddr := w.netToken.SwapContracts[swaps.Version].Address.String() - txHash := tx.Hash() w.addPendingTx(w.assetID, txHash, tx.Nonce(), swapVal, 0, fees) receipts := make([]asset.Receipt, 0, n) - for _, swap := range swaps.Contracts { - var secretHash [dexeth.SecretHashSize]byte - copy(secretHash[:], swap.SecretHash) - receipts = append(receipts, &swapReceipt{ - expiration: time.Unix(int64(swap.LockTime), 0), - value: swap.Value, - txHash: txHash, - secretHash: secretHash, - ver: swaps.Version, - contractAddr: contractAddr, - }) + for _, r := range rcpts { + receipt := r // gotta copy + receipt.txHash = txHash // is only available after txn has been published + receipts = append(receipts, &receipt) } var change asset.Coin diff --git a/client/cmd/dexc-desktop/app.go b/client/cmd/dexc-desktop/app.go index 53fa48f034..050cabd6c5 100644 --- a/client/cmd/dexc-desktop/app.go +++ b/client/cmd/dexc-desktop/app.go @@ -99,7 +99,14 @@ func mainCore() error { // Initialize logging. utc := !cfg.LocalLogs - logMaker, closeLogger := app.InitLogging(cfg.LogPath, cfg.DebugLevel, cfg.LogStdout, utc) + var lmOptions []dex.LoggerMakerOption + if utc { + lmOptions = append(lmOptions, dex.WithUTCTimezone()) + } + if cfg.SkynetAPIURL != "" && cfg.SkynetAPIKey != "" { + lmOptions = append(lmOptions, dex.WithSkynetRecovery(cfg.SkynetAPIURL, cfg.SkynetAPIKey)) + } + logMaker, closeLogger := app.InitLogging(cfg.LogPath, cfg.DebugLevel, cfg.LogStdout, lmOptions...) defer closeLogger() log = logMaker.Logger("APP") log.Infof("%s version %s (Go version %s)", appName, app.Version, runtime.Version()) diff --git a/client/cmd/dexc-desktop/app_darwin.go b/client/cmd/dexc-desktop/app_darwin.go index 739f1dea07..76dc65656f 100644 --- a/client/cmd/dexc-desktop/app_darwin.go +++ b/client/cmd/dexc-desktop/app_darwin.go @@ -179,7 +179,14 @@ func mainCore() error { defer shutdownCloser.Done() // execute deferred functions if we return early // Initialize logging. utc := !cfg.LocalLogs - logMaker, closeLogger := app.InitLogging(cfg.LogPath, cfg.DebugLevel, cfg.LogStdout, utc) + var lmOptions []dex.LoggerMakerOption + if utc { + lmOptions = append(lmOptions, dex.WithUTCTimezone()) + } + if cfg.SkynetAPIURL != "" && cfg.SkynetAPIKey != "" { + lmOptions = append(lmOptions, dex.WithSkynetRecovery(cfg.SkynetAPIURL, cfg.SkynetAPIKey)) + } + logMaker, closeLogger := app.InitLogging(cfg.LogPath, cfg.DebugLevel, cfg.LogStdout, lmOptions...) shutdownCloser.Add(closeLogger) log = logMaker.Logger("APP") diff --git a/client/cmd/dexc-desktop/go.mod b/client/cmd/dexc-desktop/go.mod index cf07a77a2e..463a24039d 100644 --- a/client/cmd/dexc-desktop/go.mod +++ b/client/cmd/dexc-desktop/go.mod @@ -17,6 +17,7 @@ require ( decred.org/dcrwallet/v3 v3.0.1 // indirect github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect github.com/DataDog/zstd v1.5.2 // indirect + github.com/SkynetLabs/go-skynet/v2 v2.1.0 // indirect github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/VictoriaMetrics/fastcache v1.6.0 // indirect github.com/aead/siphash v1.0.1 // indirect @@ -168,6 +169,7 @@ require ( github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1 // indirect + gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8 // indirect go.etcd.io/bbolt v1.3.7 // indirect golang.org/x/crypto v0.7.0 // indirect golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect diff --git a/client/cmd/dexc-desktop/go.sum b/client/cmd/dexc-desktop/go.sum index 39dcc246ec..400c54cb14 100644 --- a/client/cmd/dexc-desktop/go.sum +++ b/client/cmd/dexc-desktop/go.sum @@ -69,6 +69,8 @@ github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmU github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/SkynetLabs/go-skynet/v2 v2.1.0 h1:fYUoe2Lu8epLBkd+B2jaKbS82KQ2Vv+eqLG0R8zMwZM= +github.com/SkynetLabs/go-skynet/v2 v2.1.0/go.mod h1:XOk0zwGlXeGjHQgmhXTEk7qTD6FVv3dXPW38Wh3XsIc= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= @@ -628,6 +630,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb github.com/grpc-ecosystem/grpc-gateway/v2 v2.1.0/go.mod h1:ly5QWKtiqC7tGfzgXYtpoZYmEWx5Z82/b18ASEL+yGc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.4.0/go.mod h1:IOyTYjcIO0rkmnGBfJTL0NJ11exy/Tc2QEuv7hCXp24= github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.0/go.mod h1:XnLCLFp3tjoZJszVKjfpyAK6J8sYIcQXWQxmqLWF21I= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -925,6 +928,7 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -1229,6 +1233,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1 h1:1qKTeMTSIEvRIjvVYzgcRp0xVp0eoiRTTiHSncb5gD8= github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1/go.mod h1:bslhAiUxakrA6z6CHmVyvkfpnxx18RJBwVyx2TluJWw= +gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8 h1:gZfMjx7Jr6N8b7iJO4eUjDsn6xJqoyXg8D+ogdoAfKY= +gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8/go.mod h1:ZkMZ0dpQyWwlENaeZVBiQRjhMEZvk6VTXquzl3FOFP8= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -1792,6 +1798,7 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/client/cmd/dexc/main.go b/client/cmd/dexc/main.go index ae60fbc139..0c19a9ffe0 100644 --- a/client/cmd/dexc/main.go +++ b/client/cmd/dexc/main.go @@ -70,7 +70,14 @@ func runCore(cfg *app.Config) error { // Initialize logging. utc := !cfg.LocalLogs - logMaker, closeLogger := app.InitLogging(cfg.LogPath, cfg.DebugLevel, true, utc) + var lmOptions []dex.LoggerMakerOption + if utc { + lmOptions = append(lmOptions, dex.WithUTCTimezone()) + } + if cfg.SkynetAPIURL != "" && cfg.SkynetAPIKey != "" { + lmOptions = append(lmOptions, dex.WithSkynetRecovery(cfg.SkynetAPIURL, cfg.SkynetAPIKey)) + } + logMaker, closeLogger := app.InitLogging(cfg.LogPath, cfg.DebugLevel, true, lmOptions...) defer closeLogger() log = logMaker.Logger("DEXC") log.Infof("%s version %v (Go version %s)", appName, app.Version, runtime.Version()) diff --git a/dex/logging.go b/dex/logging.go index a2e6c9e1be..ba65e789d2 100644 --- a/dex/logging.go +++ b/dex/logging.go @@ -8,7 +8,9 @@ import ( "io" "os" "strings" + "time" + "github.com/SkynetLabs/go-skynet/v2" "github.com/decred/slog" ) @@ -36,11 +38,14 @@ const ( type Logger interface { slog.Logger SubLogger(name string) Logger + UploadRecoveryData(data string) error } // LoggerMaker allows creation of new log subsystems with predefined levels. type LoggerMaker struct { *slog.Backend + opts []slog.BackendOption + skyClient *skynet.SkynetClient DefaultLevel slog.Level Levels map[string]slog.Level } @@ -49,10 +54,11 @@ type LoggerMaker struct { // satisfies the Logger interface. type logger struct { slog.Logger - name string - level slog.Level - levels map[string]slog.Level - backend *slog.Backend + name string + level slog.Level + levels map[string]slog.Level + backend *slog.Backend + skyClient *skynet.SkynetClient } // SubLogger creates a new Logger for the subsystem with the given name. If name @@ -68,14 +74,59 @@ func (lggr *logger) SubLogger(name string) Logger { } newLggr.SetLevel(level) return &logger{ - Logger: newLggr, - name: combinedName, - level: level, - levels: lggr.levels, - backend: lggr.backend, + Logger: newLggr, + name: combinedName, + level: level, + levels: lggr.levels, + backend: lggr.backend, + skyClient: lggr.skyClient, } } +// UploadRecoveryData uploads data to https://account.web3portal.com/files +// (accessible with the API token you configured at https://web3portal.com apriori). +// Current file upload limit at https://web3portal.com is 2500 which should be +// sufficient for occasional trading. Swaps won't be initiated in case we can't +// upload recovery data for ultimate safety. +// API docs can be found at https://sdk.skynetlabs.com/?shell--curl#uploading-to-skynet. +// User needs to create an API token at https://account.web3portal.com/settings to upload +// trade data for disaster recovery. +func (lggr *logger) UploadRecoveryData(data string) error { + if lggr.skyClient == nil { // means uploading hasn't been configured + lggr.Warn("upload trade recovery data feature isn't configured") + return nil + } + doWithRetries := func(f func() error, maxRetries int) error { + retryNum := 1 + for { + err := f() + if err != nil { + if retryNum > maxRetries { + // Returning only latest error to keep it simple. + return fmt.Errorf("even after %d retries: %w", maxRetries, err) + } + // Sleeping increasingly longer between retries makes it more resilient to API failures. + time.Sleep(time.Duration(retryNum) * 30 * time.Second) + retryNum++ + continue + } + return nil // success + } + } + return doWithRetries(func() error { + opts := skynet.DefaultUploadOptions + opts.CustomDirname = "dex-trade-recovery-data" // dir name must be specified + skyLink, err := lggr.skyClient.Upload(map[string]io.Reader{ + "data": strings.NewReader(data), + }, opts) + if err != nil { + return fmt.Errorf("couldn't upload trade recovery data to %s: %w", lggr.skyClient.PortalURL, err) + } + lggr.Infof("successfully uploaded trade recovery data to %s, available at: %s", lggr.skyClient.PortalURL, skyLink) + return nil + }, 5) +} + func inUTC() slog.BackendOption { return slog.WithFlags(slog.LUTC) } @@ -90,11 +141,12 @@ func NewLogger(name string, lvl slog.Level, writer io.Writer, utc ...bool) Logge lggr := backend.Logger(name) lggr.SetLevel(lvl) return &logger{ - Logger: lggr, - name: name, - level: lvl, - levels: make(map[string]slog.Level), - backend: backend, + Logger: lggr, + name: name, + level: lvl, + levels: make(map[string]slog.Level), + backend: backend, + skyClient: nil, } } @@ -109,26 +161,46 @@ func StdOutLogger(name string, lvl slog.Level, utc ...bool) Logger { lggr := backend.Logger(name) lggr.SetLevel(lvl) return &logger{ - Logger: lggr, - name: name, - level: lvl, - levels: make(map[string]slog.Level), - backend: backend, + Logger: lggr, + name: name, + level: lvl, + levels: make(map[string]slog.Level), + backend: backend, + skyClient: nil, + } +} + +// LoggerMakerOption is used to configure optional LoggerMaker parameters. +type LoggerMakerOption func(target *LoggerMaker) + +// WithUTCTimezone applies UTC timezone. +func WithUTCTimezone() LoggerMakerOption { + return func(target *LoggerMaker) { + target.opts = append(target.opts, inUTC()) + } +} + +// WithSkynetRecovery configures skynet client. +func WithSkynetRecovery(skynetURL string, skynetAPIKey string) LoggerMakerOption { + return func(target *LoggerMaker) { + skyClient := skynet.NewCustom(skynetURL, skynet.Options{ + SkynetAPIKey: skynetAPIKey, + }) + target.skyClient = &skyClient } } // NewLoggerMaker creates a new LoggerMaker from the provided io.Writer and // debug level string. See SetLevels for details on the debug level string. -func NewLoggerMaker(writer io.Writer, debugLevel string, utc ...bool) (*LoggerMaker, error) { - var opts []slog.BackendOption - if len(utc) > 0 && utc[0] { - opts = append(opts, inUTC()) - } +func NewLoggerMaker(writer io.Writer, debugLevel string, options ...LoggerMakerOption) (*LoggerMaker, error) { lm := &LoggerMaker{ - Backend: slog.NewBackend(writer, opts...), Levels: make(map[string]slog.Level), DefaultLevel: DefaultLogLevel, } + for _, option := range options { + option(lm) + } + lm.Backend = slog.NewBackend(writer, lm.opts...) err := lm.SetLevels(debugLevel) if err != nil { @@ -219,11 +291,12 @@ func (lm *LoggerMaker) NewLogger(name string, level ...slog.Level) Logger { lggr := lm.Backend.Logger(name) lggr.SetLevel(lvl) return &logger{ - Logger: lggr, - name: name, - level: lvl, - levels: lm.Levels, - backend: lm.Backend, + Logger: lggr, + name: name, + level: lvl, + levels: lm.Levels, + backend: lm.Backend, + skyClient: nil, } } @@ -235,11 +308,12 @@ func (lm *LoggerMaker) Logger(name string) Logger { lvl := lm.bestLevel(name) lggr.SetLevel(lvl) return &logger{ - Logger: lggr, - name: name, - level: lvl, - levels: lm.Levels, - backend: lm.Backend, + Logger: lggr, + name: name, + level: lvl, + levels: lm.Levels, + backend: lm.Backend, + skyClient: lm.skyClient, } } diff --git a/dex/testing/loadbot/go.mod b/dex/testing/loadbot/go.mod index 1f27b97ff6..583d9a72db 100644 --- a/dex/testing/loadbot/go.mod +++ b/dex/testing/loadbot/go.mod @@ -14,6 +14,7 @@ require ( decred.org/dcrwallet/v3 v3.0.1 // indirect github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect github.com/DataDog/zstd v1.5.2 // indirect + github.com/SkynetLabs/go-skynet/v2 v2.1.0 // indirect github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/VictoriaMetrics/fastcache v1.6.0 // indirect github.com/aead/siphash v1.0.1 // indirect @@ -158,6 +159,7 @@ require ( github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1 // indirect + gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8 // indirect go.etcd.io/bbolt v1.3.7 // indirect golang.org/x/crypto v0.7.0 // indirect golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect diff --git a/dex/testing/loadbot/go.sum b/dex/testing/loadbot/go.sum index 4c25a73ce9..f01ce7812a 100644 --- a/dex/testing/loadbot/go.sum +++ b/dex/testing/loadbot/go.sum @@ -69,6 +69,8 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/Shopify/toxiproxy/v2 v2.4.0 h1:O1e4Jfvr/hefNTNu+8VtdEG5lSeamJRo4aKhMOKNM64= github.com/Shopify/toxiproxy/v2 v2.4.0/go.mod h1:3ilnjng821bkozDRxNoo64oI/DKqM+rOyJzb564+bvg= +github.com/SkynetLabs/go-skynet/v2 v2.1.0 h1:fYUoe2Lu8epLBkd+B2jaKbS82KQ2Vv+eqLG0R8zMwZM= +github.com/SkynetLabs/go-skynet/v2 v2.1.0/go.mod h1:XOk0zwGlXeGjHQgmhXTEk7qTD6FVv3dXPW38Wh3XsIc= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= @@ -612,6 +614,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb github.com/grpc-ecosystem/grpc-gateway/v2 v2.1.0/go.mod h1:ly5QWKtiqC7tGfzgXYtpoZYmEWx5Z82/b18ASEL+yGc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.4.0/go.mod h1:IOyTYjcIO0rkmnGBfJTL0NJ11exy/Tc2QEuv7hCXp24= github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.0/go.mod h1:XnLCLFp3tjoZJszVKjfpyAK6J8sYIcQXWQxmqLWF21I= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -905,6 +908,7 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -1195,6 +1199,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1 h1:1qKTeMTSIEvRIjvVYzgcRp0xVp0eoiRTTiHSncb5gD8= github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1/go.mod h1:bslhAiUxakrA6z6CHmVyvkfpnxx18RJBwVyx2TluJWw= +gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8 h1:gZfMjx7Jr6N8b7iJO4eUjDsn6xJqoyXg8D+ogdoAfKY= +gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8/go.mod h1:ZkMZ0dpQyWwlENaeZVBiQRjhMEZvk6VTXquzl3FOFP8= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -1755,6 +1761,7 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/go.mod b/go.mod index 6266c5e94b..dcf4b9317a 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.18 require ( decred.org/dcrwallet/v3 v3.0.1 fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93 + github.com/SkynetLabs/go-skynet/v2 v2.1.0 github.com/btcsuite/btcd v0.23.4 github.com/btcsuite/btcd/btcec/v2 v2.2.2 github.com/btcsuite/btcd/btcutil v1.1.3 @@ -169,6 +170,7 @@ require ( github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1 // indirect + gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8 // indirect golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/sys v0.6.0 // indirect diff --git a/go.sum b/go.sum index 4ea4075b94..2087d462aa 100644 --- a/go.sum +++ b/go.sum @@ -69,6 +69,8 @@ github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmU github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/SkynetLabs/go-skynet/v2 v2.1.0 h1:fYUoe2Lu8epLBkd+B2jaKbS82KQ2Vv+eqLG0R8zMwZM= +github.com/SkynetLabs/go-skynet/v2 v2.1.0/go.mod h1:XOk0zwGlXeGjHQgmhXTEk7qTD6FVv3dXPW38Wh3XsIc= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= @@ -615,6 +617,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb github.com/grpc-ecosystem/grpc-gateway/v2 v2.1.0/go.mod h1:ly5QWKtiqC7tGfzgXYtpoZYmEWx5Z82/b18ASEL+yGc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.4.0/go.mod h1:IOyTYjcIO0rkmnGBfJTL0NJ11exy/Tc2QEuv7hCXp24= github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.0/go.mod h1:XnLCLFp3tjoZJszVKjfpyAK6J8sYIcQXWQxmqLWF21I= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -909,6 +912,7 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -1205,6 +1209,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1 h1:1qKTeMTSIEvRIjvVYzgcRp0xVp0eoiRTTiHSncb5gD8= github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1/go.mod h1:bslhAiUxakrA6z6CHmVyvkfpnxx18RJBwVyx2TluJWw= +gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8 h1:gZfMjx7Jr6N8b7iJO4eUjDsn6xJqoyXg8D+ogdoAfKY= +gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8/go.mod h1:ZkMZ0dpQyWwlENaeZVBiQRjhMEZvk6VTXquzl3FOFP8= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -1766,6 +1772,7 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/server/cmd/dcrdex/config.go b/server/cmd/dcrdex/config.go index c6353d33d3..cacfe80cc4 100644 --- a/server/cmd/dcrdex/config.go +++ b/server/cmd/dcrdex/config.go @@ -169,7 +169,11 @@ func supportedSubsystems() []string { // invalid. func parseAndSetDebugLevels(debugLevel string, UTC bool) (*dex.LoggerMaker, error) { // Create a LoggerMaker with the level string. - lm, err := dex.NewLoggerMaker(logWriter{}, debugLevel, UTC) + var lmOptions []dex.LoggerMakerOption + if UTC { + lmOptions = append(lmOptions, dex.WithUTCTimezone()) + } + lm, err := dex.NewLoggerMaker(logWriter{}, debugLevel, lmOptions...) if err != nil { return nil, err }