Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate DCRDEX for DEX onboarding page #238

Merged
merged 11 commits into from
Dec 13, 2023
33 changes: 15 additions & 18 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type config struct {
Quiet bool `short:"q" long:"quiet" description:"Easy way to set debuglevel to error"`
SpendUnconfirmed bool `long:"spendunconfirmed" description:"Allow the assetsManager to use transactions that have not been confirmed"`
Profile int `long:"profile" description:"Runs local web server for profiling"`

net libutils.NetworkType
}

func defaultConfig(defaultHomeDir string) config {
Expand Down Expand Up @@ -158,7 +160,6 @@ func loadConfig() (*config, error) {
if _, err := os.Stat(cfg.ConfigFile); os.IsNotExist(err) {
// Non-default config file must exist
if defaultCfg.ConfigFile != cfg.ConfigFile {
fmt.Fprintln(os.Stderr, err)
return loadConfigError(err)
}
// Warn about missing default config file, but continue
Expand All @@ -169,7 +170,6 @@ func loadConfig() (*config, error) {
err = flags.NewIniParser(parser).ParseFile(cfg.ConfigFile)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
return loadConfigError(err)
}
Expand Down Expand Up @@ -200,10 +200,7 @@ func loadConfig() (*config, error) {
}
}

str := "%s: failed to create home directory: %v"
err := fmt.Errorf(str, funcName, err)
fmt.Fprintln(os.Stderr, err)
return nil, err
return nil, fmt.Errorf("%s: failed to create home directory: %v", funcName, err)
}

// If a non-default appdata folder is specified, it may be necessary to
Expand All @@ -218,7 +215,6 @@ func loadConfig() (*config, error) {
// succeeds. This prevents the warning on help messages and invalid
// options.
if configFileError != nil {
fmt.Printf("%v\n", configFileError)
return loadConfigError(configFileError)
}

Expand All @@ -230,28 +226,29 @@ func loadConfig() (*config, error) {
if cfg.MaxLogZips < 0 {
cfg.MaxLogZips = 0
}
net := cfg.Network
if net == "testnet" {
net = "testnet3"
}
logDir := filepath.Join(cfg.LogDir, net)
itswisdomagain marked this conversation as resolved.
Show resolved Hide resolved
initLogRotator(logDir, cfg.MaxLogZips)

// Special show command to list supported subsystems and exit.
if cfg.DebugLevel == "show" {
fmt.Println("Supported subsystems", supportedSubsystems())
os.Exit(0)
cfg.net = libutils.ToNetworkType(cfg.Network)
if cfg.net == libutils.Unknown {
return loadConfigError(fmt.Errorf("network type is not supported: %s", cfg.Network))
}

// Parse, validate, and set debug log level(s).
if cfg.Quiet {
cfg.DebugLevel = "error"
}

cfg.Network = string(cfg.net)
cfg.LogDir = filepath.Join(cfg.LogDir, cfg.Network)

// Special show command to list supported subsystems and exit.
if cfg.DebugLevel == "show" {
fmt.Println("Supported subsystems", supportedSubsystems())
os.Exit(0)
}

// Parse, validate, and set debug log level(s).
if err := parseAndSetDebugLevels(cfg.DebugLevel); err != nil {
err = fmt.Errorf("%s: %v", funcName, err.Error())
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
return loadConfigError(err)
}
Expand Down
128 changes: 128 additions & 0 deletions dexc/core.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package dexc

import (
"context"
"fmt"
"path/filepath"
"sync"
"time"

"decred.org/dcrdex/client/core"
"decred.org/dcrdex/dex"
libutils "github.com/crypto-power/cryptopower/libwallet/utils"
)

// DEXClient represents the Decred DEX client and embeds *core.Core.
type DEXClient struct {
*core.Core

shutdownChan <-chan struct{}
bondBufferCache sync.Map
log dex.Logger
}

func (dc *DEXClient) SetDEXPassword(pw []byte, seed []byte) error {
return dc.InitializeClient(pw, seed)
}

func (dc *DEXClient) IsDEXPasswordSet() bool {
return dc.IsInitialized()
}

// WaitForShutdown returns a chan that will be closed if core exits.
func (dc *DEXClient) WaitForShutdown() <-chan struct{} {
return dc.shutdownChan
}

type valStamp struct {
val uint64
stamp time.Time
}

// BondsFeeBuffer is a caching helper for the bonds fee buffer to assist the
// frontend by stabilizing this value for up to 45 minutes from the last request
// for a given asset and because (*Core).BondsFeeBuffer returns a fresh fee
// buffer based on a current (but padded) fee rate estimate. Values for a given
// asset are cached for 45 minutes. These values are meant to provide a sensible
// but well-padded fee buffer for bond transactions now and well into the
// future, so a long expiry is appropriate.
func (dc *DEXClient) BondsFeeBuffer(assetID uint32) uint64 {
const expiry = 45 * time.Minute

buf, ok := dc.bondBufferCache.Load(assetID)
var cachedFeeBuffer valStamp
if ok {
cachedFeeBuffer = buf.(valStamp)
}

if ok && time.Since(cachedFeeBuffer.stamp) > expiry {
dc.log.Tracef("Using cached bond fee buffer (%v old): %d", time.Since(cachedFeeBuffer.stamp), cachedFeeBuffer.val)
return cachedFeeBuffer.val
}

feeBuffer, err := dc.Core.BondsFeeBuffer(assetID)
if err != nil {
dc.log.Error("Error fetching bond fee buffer: %v", err)
return 0
}

dc.log.Tracef("Obtained fresh bond fee buffer: %d", feeBuffer)
dc.bondBufferCache.Store(assetID, valStamp{feeBuffer, time.Now()})
return feeBuffer
}

func Start(ctx context.Context, root, lang, logDir, logLvl string, net libutils.NetworkType, maxLogZips int) (*DEXClient, error) {
dexNet, err := parseDEXNet(net)
if err != nil {
return nil, fmt.Errorf("error parsing network: %w", err)
}

logger, logCloser, err := newDexLogger(logDir, logLvl, maxLogZips)
if err != nil {
return nil, err
}

dbPath := filepath.Join(root, "dexc.db")
cfg := &core.Config{
DBPath: dbPath,
Net: dexNet,
Logger: logger,
Language: lang,
UnlockCoinsOnLogin: false, // TODO: Make configurable.
}

clientCore, err := core.New(cfg)
if err != nil {
return nil, fmt.Errorf("failed to initialize dex core: %w", err)
}

shutdownChan := make(chan struct{})
dc := &DEXClient{
Core: clientCore,
shutdownChan: shutdownChan,
log: logger,
}

// Use a goroutine to start dex core as it'll block until dex core exits.
go func() {
dc.Run(ctx)
close(shutdownChan)
dc.Core = nil
logCloser()
}()

return dc, nil
}

func parseDEXNet(net libutils.NetworkType) (dex.Network, error) {
switch net {
case libutils.Mainnet:
return dex.Mainnet, nil
case libutils.Testnet:
return dex.Testnet, nil
case libutils.Regression, libutils.Simulation:
return dex.Simnet, nil
default:
return 0, fmt.Errorf("unknown network %s", net)
}
}
37 changes: 37 additions & 0 deletions dexc/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package dexc

import (
"fmt"
"os"
"path/filepath"

"decred.org/dcrdex/dex"
libutils "github.com/crypto-power/cryptopower/libwallet/utils"
"github.com/jrick/logrotate/rotator"
)

var dexLogFile = "dexc.log"

// newDexLogger initializes a new dex.Logger.
func newDexLogger(logDir, lvl string, maxRolls int) (dex.Logger, func(), error) {
err := os.MkdirAll(logDir, libutils.UserFilePerm)
if err != nil {
return nil, nil, fmt.Errorf("failed to create log directory: %w", err)
}

r, err := rotator.New(filepath.Join(logDir, dexLogFile), 32*1024, false, maxRolls)
if err != nil {
return nil, nil, fmt.Errorf("failed to create file rotator: %w", err)
}

logCloser := func() {
r.Close()
}

l, err := dex.NewLoggerMaker(r, lvl, true /* TODO: Make configurable */)
if err != nil {
return nil, nil, fmt.Errorf("Failed to initialize log: %w", err)
}

return l.NewLogger("DEXC"), logCloser, nil
}