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

asset{btc/ltc}: Add custom wallet constructor for BTC and LTC #2636

Merged
merged 6 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions client/asset/bch/spv.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ import (
)

const (
DefaultM uint64 = 784931 // From bchutil. Used for gcs filters.
logDirName = "logs"
neutrinoDBName = "neutrino.db"
defaultAcctNum = 0
DefaultM uint64 = 784931 // From bchutil. Used for gcs filters.
logDirName = "logs"
neutrinoDBName = "neutrino.db"
defaultAcctNum = 0
defaultAcctName = "default"
)

var (
Expand Down Expand Up @@ -270,6 +271,15 @@ func (w *bchSPVWallet) txDetails(txHash *bchchainhash.Hash) (*bchwtxmgr.TxDetail

var _ btc.BTCWallet = (*bchSPVWallet)(nil)

// AccountInfo returns the account information of the wallet for use by the
// exchange wallet.
func (w *bchSPVWallet) AccountInfo() btc.XCWalletAccount {
return btc.XCWalletAccount{
AccountName: defaultAcctName,
AccountNumber: defaultAcctNum,
}
}

func (w *bchSPVWallet) PublishTransaction(btcTx *wire.MsgTx, label string) error {
bchTx, err := convertMsgTxToBCH(btcTx)
if err != nil {
Expand Down
45 changes: 42 additions & 3 deletions client/asset/btc/btc.go
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,29 @@ func (d *Driver) Info() *asset.WalletInfo {
return WalletInfo
}

type CustomSPVWalletConstructor func(settings map[string]string, params *chaincfg.Params) (BTCWallet, error)

// customSPVWalletConstructors are functions for setting up custom
// implementations of the BTCWallet interface that may be used by the
// ExchangeWalletSPV instead of the default spv implementation.
var customSPVWalletConstructors = map[string]CustomSPVWalletConstructor{}

// RegisterCustomSPVWallet registers a function that should be used in creating
// a BTCWallet implementation that the ExchangeWalletSPV will use in place of
// the default spv wallet implementation. External consumers can use this
// function to provide alternative BTCWallet implementations, and must do so
// before attempting to create an ExchangeWalletSPV instance of this type. It'll
// panic if callers try to register a wallet twice.
func RegisterCustomSPVWallet(constructor CustomSPVWalletConstructor, def *asset.WalletDefinition) {
for _, availableWallets := range WalletInfo.AvailableWallets {
if def.Type == availableWallets.Type {
panic(fmt.Sprintf("wallet type (%q) is already registered", def.Type))
}
}
customSPVWalletConstructors[def.Type] = constructor
WalletInfo.AvailableWallets = append(WalletInfo.AvailableWallets, def)
}

// swapOptions captures the available Swap options. Tagged to be used with
// config.Unmapify to decode e.g. asset.Order.Options.
type swapOptions struct {
Expand Down Expand Up @@ -1001,7 +1024,22 @@ func NewWallet(cfg *asset.WalletConfig, logger dex.Logger, net dex.Network) (ass
cloneCFG.Ports = dexbtc.NetPorts{} // no default ports
return ElectrumWallet(cloneCFG)
default:
return nil, fmt.Errorf("unknown wallet type %q", cfg.Type)
makeCustomWallet, ok := customSPVWalletConstructors[cfg.Type]
if !ok {
return nil, fmt.Errorf("unknown wallet type %q", cfg.Type)
}

// Create custom wallet first and return early if we encounter any
// error.
btcWallet, err := makeCustomWallet(cfg.Settings, cloneCFG.ChainParams)
if err != nil {
return nil, fmt.Errorf("btc custom wallet setup error: %v", err)
}

walletConstructor := func(_ string, _ *WalletConfig, _ *chaincfg.Params, _ dex.Logger) BTCWallet {
return btcWallet
}
return OpenSPVWallet(cloneCFG, walletConstructor)
}
}

Expand Down Expand Up @@ -1258,8 +1296,6 @@ func OpenSPVWallet(cfg *BTCCloneCFG, walletConstructor BTCWalletConstructor) (*E
spvw := &spvWallet{
chainParams: cfg.ChainParams,
cfg: walletCfg,
acctNum: defaultAcctNum,
acctName: defaultAcctName,
dir: filepath.Join(cfg.WalletCFG.DataDir, cfg.ChainParams.Name),
txBlocks: make(map[chainhash.Hash]*hashEntry),
checkpoints: make(map[OutPoint]*scanCheckpoint),
Expand All @@ -1271,6 +1307,9 @@ func OpenSPVWallet(cfg *BTCCloneCFG, walletConstructor BTCWalletConstructor) (*E
spvw.wallet = walletConstructor(spvw.dir, spvw.cfg, spvw.chainParams, spvw.log)
btc.setNode(spvw)

// Set account number for easy reference.
spvw.acctNum = spvw.wallet.AccountInfo().AccountNumber

w := &ExchangeWalletSPV{
intermediaryWallet: &intermediaryWallet{
baseWallet: btc,
Expand Down
9 changes: 9 additions & 0 deletions client/asset/btc/spv.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ func openSPVWallet(dir string, cfg *WalletConfig,
return w
}

// AccountInfo returns the account information of the wallet for use by the
// exchange wallet.
func (w *btcSPVWallet) AccountInfo() XCWalletAccount {
return XCWalletAccount{
AccountName: defaultAcctName,
AccountNumber: defaultAcctNum,
}
}

func (w *btcSPVWallet) Birthday() time.Time {
return w.birthdayV.Load().(time.Time)
}
Expand Down
7 changes: 7 additions & 0 deletions client/asset/btc/spv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ type tBtcWallet struct {
*testData
}

func (c *tBtcWallet) AccountInfo() XCWalletAccount {
return XCWalletAccount{
AccountName: defaultAcctName,
AccountNumber: defaultAcctNum,
}
}

func (c *tBtcWallet) ListSinceBlock(start, end, syncHeight int32) ([]btcjson.ListTransactionsResult, error) {
return nil, nil
}
Expand Down
13 changes: 10 additions & 3 deletions client/asset/btc/spv_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ type BTCWallet interface {
WaitForShutdown()
ChainSynced() bool // currently unused
AccountProperties(scope waddrmgr.KeyScope, acct uint32) (*waddrmgr.AccountProperties, error)
// AccountInfo returns the account information of the wallet for use by the
// exchange wallet.
AccountInfo() XCWalletAccount
// The below methods are not implemented by *wallet.Wallet, so must be
// implemented by the BTCWallet implementation.
WalletTransaction(txHash *chainhash.Hash) (*wtxmgr.TxDetails, error)
Expand All @@ -120,6 +123,11 @@ type BTCWallet interface {
ListSinceBlock(start, end, syncHeight int32) ([]btcjson.ListTransactionsResult, error)
}

type XCWalletAccount struct {
AccountName string
AccountNumber uint32
}

// BlockNotification is block hash and height delivered by a BTCWallet when it
// is finished processing a block.
type BlockNotification struct {
Expand Down Expand Up @@ -244,7 +252,6 @@ type spvWallet struct {
wallet BTCWallet
cl SPVService
acctNum uint32
acctName string
dir string
decodeAddr dexbtc.AddressDecoder

Expand Down Expand Up @@ -607,7 +614,7 @@ func (w *spvWallet) listTransactionsSinceBlock(blockHeight int32) ([]btcjson.Lis
// balances retrieves a wallet's balance details.
func (w *spvWallet) balances() (*GetBalancesResult, error) {
// Determine trusted vs untrusted coins with listunspent.
unspents, err := w.wallet.ListUnspent(0, math.MaxInt32, w.acctName)
unspents, err := w.wallet.ListUnspent(0, math.MaxInt32, w.wallet.AccountInfo().AccountName)
if err != nil {
return nil, fmt.Errorf("error listing unspent outputs: %w", err)
}
Expand Down Expand Up @@ -644,7 +651,7 @@ func (w *spvWallet) balances() (*GetBalancesResult, error) {

// listUnspent retrieves list of the wallet's UTXOs.
func (w *spvWallet) listUnspent() ([]*ListUnspentResult, error) {
unspents, err := w.wallet.ListUnspent(0, math.MaxInt32, w.acctName)
unspents, err := w.wallet.ListUnspent(0, math.MaxInt32, w.wallet.AccountInfo().AccountName)
if err != nil {
return nil, err
}
Expand Down
14 changes: 8 additions & 6 deletions client/asset/dcr/dcr.go
Original file line number Diff line number Diff line change
Expand Up @@ -744,14 +744,16 @@ func NewWallet(cfg *asset.WalletConfig, logger dex.Logger, network dex.Network)
return nil, err
}
default:
if makeCustomWallet, ok := customWalletConstructors[cfg.Type]; ok {
dcr.wallet, err = makeCustomWallet(cfg.Settings, chainParams, logger)
if err != nil {
return nil, fmt.Errorf("custom wallet setup error: %v", err)
}
} else {
makeCustomWallet, ok := customWalletConstructors[cfg.Type]
if !ok {
return nil, fmt.Errorf("unknown wallet type %q", cfg.Type)
}

// Create custom wallet and return early if we encounter any error.
dcr.wallet, err = makeCustomWallet(cfg.Settings, chainParams, logger)
if err != nil {
return nil, fmt.Errorf("custom wallet setup error: %v", err)
}
}

return w, nil
Expand Down
8 changes: 4 additions & 4 deletions client/asset/dcr/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ var customWalletConstructors = map[string]WalletConstructor{}
// Wallet implementation that the ExchangeWallet of the specified type will use
// in place of the default rpcWallet implementation. External consumers can use
// this function to provide alternative Wallet implementations, and must do so
// before attempting to create an ExchangeWallet instance of this type.
func RegisterCustomWallet(constructor WalletConstructor, def *asset.WalletDefinition) error {
// before attempting to create an ExchangeWallet instance of this type. It'll
// panic if callers try to register a wallet twice.
func RegisterCustomWallet(constructor WalletConstructor, def *asset.WalletDefinition) {
for _, availableWallets := range WalletInfo.AvailableWallets {
if def.Type == availableWallets.Type {
return fmt.Errorf("already support %q wallets", def.Type)
panic(fmt.Sprintf("wallet type (%q) already registered", def.Type))
}
}
customWalletConstructors[def.Type] = constructor
WalletInfo.AvailableWallets = append(WalletInfo.AvailableWallets, def)
return nil
}

type TipChangeCallback func(context.Context, *chainhash.Hash, int64, error)
Expand Down
2 changes: 1 addition & 1 deletion client/asset/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ type WalletConfig struct {
// wallet/node peers changes, or the wallet fails to get the count. This
// should not be called prior to Connect of the constructed wallet.
PeersChange func(uint32, error)
// DataDir is a filesystem directory the the wallet may use for persistent
// DataDir is a filesystem directory the wallet may use for persistent
// storage.
DataDir string
}
Expand Down
38 changes: 37 additions & 1 deletion client/asset/ltc/ltc.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,27 @@ func (d *Driver) Create(params *asset.CreateWalletParams) error {
params.Logger, recoveryCfg.NumExternalAddresses, recoveryCfg.NumInternalAddresses, chainParams)
}

// customSPVWalletConstructors are functions for setting up custom
// implementations of the btc.BTCWallet interface that may be used by the
// ExchangeWalletSPV instead of the default spv implementation.
var customSPVWalletConstructors = map[string]btc.CustomSPVWalletConstructor{}

// RegisterCustomSPVWallet registers a function that should be used in creating
// a btc.BTCWallet implementation that the ExchangeWalletSPV will use in place
// of the default spv wallet implementation. External consumers can use this
// function to provide alternative btc.BTCWallet implementations, and must do so
// before attempting to create an ExchangeWalletSPV instance of this type. It'll
// panic if callers try to register a wallet twice.
func RegisterCustomSPVWallet(constructor btc.CustomSPVWalletConstructor, def *asset.WalletDefinition) {
for _, availableWallets := range WalletInfo.AvailableWallets {
if def.Type == availableWallets.Type {
panic(fmt.Sprintf("wallet type (%q) is already registered", def.Type))
}
}
customSPVWalletConstructors[def.Type] = constructor
WalletInfo.AvailableWallets = append(WalletInfo.AvailableWallets, def)
}

// NewWallet is the exported constructor by which the DEX will import the
// exchange wallet.
func NewWallet(cfg *asset.WalletConfig, logger dex.Logger, network dex.Network) (asset.Wallet, error) {
Expand Down Expand Up @@ -204,7 +225,22 @@ func NewWallet(cfg *asset.WalletConfig, logger dex.Logger, network dex.Network)
cloneCFG.Ports = dexbtc.NetPorts{} // no default ports
return btc.ElectrumWallet(cloneCFG)
default:
return nil, fmt.Errorf("unknown wallet type %q", cfg.Type)
makeCustomWallet, ok := customSPVWalletConstructors[cfg.Type]
if !ok {
return nil, fmt.Errorf("unknown wallet type %q", cfg.Type)
}

// Create custom wallet first and return early if we encounter any
// error.
ltcWallet, err := makeCustomWallet(cfg.Settings, cloneCFG.ChainParams)
if err != nil {
return nil, fmt.Errorf("custom wallet setup error: %v", err)
}

walletConstructor := func(_ string, _ *btc.WalletConfig, _ *chaincfg.Params, _ dex.Logger) btc.BTCWallet {
return ltcWallet
}
return btc.OpenSPVWallet(cloneCFG, walletConstructor)
}
}

Expand Down
20 changes: 15 additions & 5 deletions client/asset/ltc/spv.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ import (
)

const (
DefaultM uint64 = 784931 // From ltcutil. Used for gcs filters.
logDirName = "logs"
neutrinoDBName = "neutrino.db"
defaultAcctNum = 0
dbTimeout = 20 * time.Second
DefaultM uint64 = 784931 // From ltcutil. Used for gcs filters.
logDirName = "logs"
neutrinoDBName = "neutrino.db"
defaultAcctNum = 0
defaultAcctName = "default"
dbTimeout = 20 * time.Second
)

var (
Expand Down Expand Up @@ -164,6 +165,15 @@ func createSPVWallet(privPass []byte, seed []byte, bday time.Time, walletDir str
return nil
}

// AccountInfo returns the account information of the wallet for use by the
// exchange wallet.
func (w *ltcSPVWallet) AccountInfo() btc.XCWalletAccount {
return btc.XCWalletAccount{
AccountName: defaultAcctName,
AccountNumber: defaultAcctNum,
}
}

// walletParams works around a bug in ltcwallet that doesn't recognize
// wire.TestNet4 in (*ScopedKeyManager).cloneKeyWithVersion which is called from
// AccountProperties. Only do this for the *wallet.Wallet, not the
Expand Down
Loading