Skip to content

Commit

Permalink
asset{btc/ltc}: Add custom wallet constructor for BTC and LTC (#2636)
Browse files Browse the repository at this point in the history
* add custom wallet constructors for BTC and LTC

Signed-off-by: Philemon Ukane <[email protected]>
  • Loading branch information
ukane-philemon authored Jan 12, 2024
1 parent 9e6a621 commit 2201c69
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 27 deletions.
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 @@ -655,6 +655,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 @@ -1002,7 +1025,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 @@ -1259,8 +1297,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 @@ -1272,6 +1308,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

0 comments on commit 2201c69

Please sign in to comment.