github.com/decred/dcrlnd@v0.7.6/lnwallet/interface.go (about)

     1  package lnwallet
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  
     9  	"decred.org/dcrwallet/v4/wallet"
    10  	"decred.org/dcrwallet/v4/wallet/txauthor"
    11  	"github.com/decred/dcrd/chaincfg/chainhash"
    12  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    13  	"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
    14  	"github.com/decred/dcrd/dcrutil/v4"
    15  	"github.com/decred/dcrd/hdkeychain/v3"
    16  	"github.com/decred/dcrd/txscript/v4/stdaddr"
    17  	"github.com/decred/dcrd/wire"
    18  	"github.com/decred/dcrlnd/btcwalletcompat"
    19  	"github.com/decred/dcrlnd/internal/psbt"
    20  	"github.com/decred/dcrlnd/keychain"
    21  	"github.com/decred/dcrlnd/lnwallet/chainfee"
    22  )
    23  
    24  const (
    25  	// DefaultAccountName is the name for the default account used to manage
    26  	// on-chain funds within the wallet.
    27  	DefaultAccountName = "default"
    28  )
    29  
    30  // AddressType is an enum-like type which denotes the possible address types
    31  // WalletController supports.
    32  type AddressType uint8
    33  
    34  const (
    35  	// WitnessPubKey represents a p2wkh address.
    36  	// TODO(decred) remove this type of address
    37  	WitnessPubKey AddressType = iota
    38  
    39  	// NestedWitnessPubKey represents a p2sh output which is itself a
    40  	// nested p2wkh output.
    41  	// TODO(decred) remove this type of address
    42  	NestedWitnessPubKey
    43  
    44  	// PubKeyHash represents a p2pkh address
    45  	PubKeyHash
    46  
    47  	// ScriptHash represents a p2sh address
    48  	ScriptHash
    49  
    50  	// UnknownAddressType represents an output with an unknown or non-standard
    51  	// script.
    52  	UnknownAddressType
    53  )
    54  
    55  var (
    56  	// DefaultPublicPassphrase is the default public passphrase used for the
    57  	// wallet.
    58  	DefaultPublicPassphrase = []byte("public")
    59  
    60  	// DefaultPrivatePassphrase is the default private passphrase used for
    61  	// the wallet.
    62  	DefaultPrivatePassphrase = []byte("hello")
    63  
    64  	// ErrDoubleSpend is returned from PublishTransaction in case the
    65  	// tx being published is spending an output spent by a conflicting
    66  	// transaction.
    67  	ErrDoubleSpend = errors.New("transaction rejected: output already spent")
    68  
    69  	// ErrNotMine is an error denoting that a WalletController instance is
    70  	// unable to spend a specified output.
    71  	ErrNotMine = errors.New("the passed output doesn't belong to the wallet")
    72  )
    73  
    74  // ErrNoOutputs is returned if we try to create a transaction with no outputs
    75  // or send coins to a set of outputs that is empty.
    76  var ErrNoOutputs = errors.New("no outputs")
    77  
    78  // LockID is equivalent to btcsuite/btcwallet/wtxmgr LockID type.
    79  type LockID [32]byte
    80  
    81  // ErrInvalidMinconf is returned if we try to create a transaction with
    82  // invalid minConfs value.
    83  var ErrInvalidMinconf = errors.New("minimum number of confirmations must " +
    84  	"be a non-negative number")
    85  
    86  // Utxo is an unspent output denoted by its outpoint, and output value of the
    87  // original output.
    88  type Utxo struct {
    89  	AddressType   AddressType
    90  	Value         dcrutil.Amount
    91  	Confirmations int64
    92  	PkScript      []byte
    93  	wire.OutPoint
    94  
    95  	// TODO(decred) this needs to include ScriptVersion. Then this version needs
    96  	// to be filled and used everywhere instead of DefaultScriptVersion.
    97  
    98  	Derivation *psbt.Bip32Derivation
    99  	PrevTx     *wire.MsgTx
   100  }
   101  
   102  // TransactionDetail describes a transaction with either inputs which belong to
   103  // the wallet, or has outputs that pay to the wallet.
   104  type TransactionDetail struct {
   105  	// Hash is the transaction hash of the transaction.
   106  	Hash chainhash.Hash
   107  
   108  	// Value is the net value of this transaction (in atoms) from the
   109  	// PoV of the wallet. If this transaction purely spends from the
   110  	// wallet's funds, then this value will be negative. Similarly, if this
   111  	// transaction credits the wallet, then this value will be positive.
   112  	Value dcrutil.Amount
   113  
   114  	// NumConfirmations is the number of confirmations this transaction
   115  	// has. If the transaction is unconfirmed, then this value will be
   116  	// zero.
   117  	NumConfirmations int32
   118  
   119  	// BlockHeight is the hash of the block which includes this
   120  	// transaction. Unconfirmed transactions will have a nil value for this
   121  	// field.
   122  	BlockHash *chainhash.Hash
   123  
   124  	// BlockHeight is the height of the block including this transaction.
   125  	// Unconfirmed transaction will show a height of zero.
   126  	BlockHeight int32
   127  
   128  	// Timestamp is the unix timestamp of the block including this
   129  	// transaction. If the transaction is unconfirmed, then this will be a
   130  	// timestamp of txn creation.
   131  	Timestamp int64
   132  
   133  	// TotalFees is the total fee in atoms paid by this transaction.
   134  	TotalFees int64
   135  
   136  	// DestAddresses are the destinations for a transaction
   137  	DestAddresses []stdaddr.Address
   138  
   139  	// RawTx returns the raw serialized transaction.
   140  	RawTx []byte
   141  
   142  	// Label is an optional transaction label.
   143  	Label string
   144  }
   145  
   146  // TransactionSubscription is an interface which describes an object capable of
   147  // receiving notifications of new transaction related to the underlying wallet.
   148  // TODO(roasbeef): add balance updates?
   149  type TransactionSubscription interface {
   150  	// ConfirmedTransactions returns a channel which will be sent on as new
   151  	// relevant transactions are confirmed.
   152  	ConfirmedTransactions() chan *TransactionDetail
   153  
   154  	// UnconfirmedTransactions returns a channel which will be sent on as
   155  	// new relevant transactions are seen within the network.
   156  	UnconfirmedTransactions() chan *TransactionDetail
   157  
   158  	// Cancel finalizes the subscription, cleaning up any resources
   159  	// allocated.
   160  	Cancel()
   161  }
   162  
   163  // WalletController defines an abstract interface for controlling a local Pure
   164  // Go wallet, a local or remote wallet via an RPC mechanism, or possibly even
   165  // a daemon assisted hardware wallet. This interface serves the purpose of
   166  // allowing LightningWallet to be seamlessly compatible with several wallets
   167  // such as: uspv, dcrwallet, etc. This interface then serves as a "base wallet",
   168  // with Lightning Network awareness taking place at a "higher" level of
   169  // abstraction. Essentially, an overlay wallet.  Implementors of this interface
   170  // must closely adhere to the documented behavior of all interface methods in
   171  // order to ensure identical behavior across all concrete implementations.
   172  type WalletController interface {
   173  	// FetchInputInfo queries for the WalletController's knowledge of the
   174  	// passed outpoint. If the base wallet determines this output is under
   175  	// its control, then the original txout should be returned.  Otherwise,
   176  	// a non-nil error value of ErrNotMine should be returned instead.
   177  	FetchInputInfo(prevOut *wire.OutPoint) (*Utxo, error)
   178  
   179  	// ScriptForOutput returns the address, witness program and redeem
   180  	// script for a given UTXO. An error is returned if the UTXO does not
   181  	// belong to our wallet or it is not a managed pubKey address.
   182  	ScriptForOutput(output *wire.TxOut) (btcwalletcompat.ManagedPubKeyAddress,
   183  		[]byte, []byte, error)
   184  
   185  	// ConfirmedBalance returns the sum of all the wallet's unspent outputs
   186  	// that have at least confs confirmations. If confs is set to zero,
   187  	// then all unspent outputs, including those currently in the mempool
   188  	// will be included in the final sum. The account parameter serves as a
   189  	// filter to retrieve the balance for a specific account. When empty,
   190  	// the confirmed balance of all wallet accounts is returned.
   191  	//
   192  	// NOTE: Only witness outputs should be included in the computation of
   193  	// the total spendable balance of the wallet. We require this as only
   194  	// witness inputs can be used for funding channels.
   195  	ConfirmedBalance(confs int32, accountFilter string) (dcrutil.Amount,
   196  		error)
   197  
   198  	// NewAddress returns the next external or internal address for the
   199  	// wallet dictated by the value of the `change` parameter. If change is
   200  	// true, then an internal address should be used, otherwise an external
   201  	// address should be returned. The type of address returned is dictated
   202  	// by the wallet's capabilities, and may be of type: p2sh, p2wkh,
   203  	// p2wsh, etc. The account parameter must be non-empty as it determines
   204  	// which account the address should be generated from.
   205  	NewAddress(addrType AddressType, change bool,
   206  		account string) (stdaddr.Address, error)
   207  
   208  	// LastUnusedAddress returns the last *unused* address known by the
   209  	// wallet. An address is unused if it hasn't received any payments.
   210  	// This can be useful in UIs in order to continually show the
   211  	// "freshest" address without having to worry about "address inflation"
   212  	// caused by continual refreshing. Similar to NewAddress it can derive
   213  	// a specified address type. By default, this is a non-change address.
   214  	// The account parameter must be non-empty as it determines which
   215  	// account the address should be generated from.
   216  	LastUnusedAddress(addrType AddressType,
   217  		account string) (stdaddr.Address, error)
   218  
   219  	// IsOurAddress checks if the passed address belongs to this wallet
   220  	IsOurAddress(a stdaddr.Address) bool
   221  
   222  	// AddressInfo returns the information about an address, if it's known
   223  	// to this wallet.
   224  	AddressInfo(a stdaddr.Address) (btcwalletcompat.ManagedAddress, error)
   225  
   226  	// ListAccounts retrieves all accounts belonging to the wallet by
   227  	// default. A name  filter can be provided to filter through all of the
   228  	// wallet accounts and return only those matching.
   229  	ListAccounts(string) ([]wallet.AccountProperties, error)
   230  
   231  	// ImportAccount imports an account backed by an account extended public
   232  	// key. The master key fingerprint denotes the fingerprint of the root
   233  	// key corresponding to the account public key (also known as the key
   234  	// with derivation path m/). This may be required by some hardware
   235  	// wallets for proper identification and signing.
   236  	ImportAccount(name string, accountPubKey *hdkeychain.ExtendedKey,
   237  		dryRun bool) (*wallet.AccountProperties, []stdaddr.Address,
   238  		[]stdaddr.Address, error)
   239  
   240  	// ImportPublicKey imports a single derived public key into the wallet.
   241  	ImportPublicKey(pubKey *secp256k1.PublicKey) error
   242  
   243  	// SendOutputs funds, signs, and broadcasts a Decred transaction paying
   244  	// out to the specified outputs. In the case the wallet has insufficient
   245  	// funds, or the outputs are non-standard, an error should be returned.
   246  	// This method also takes the target fee expressed in atoms/kB that should
   247  	// be used when crafting the transaction.
   248  	//
   249  	// NOTE: This method requires the global coin selection lock to be held.
   250  	SendOutputs(outputs []*wire.TxOut, feeRate chainfee.AtomPerKByte,
   251  		minConfs int32, label, fromAccount string) (*wire.MsgTx, error)
   252  
   253  	// CreateSimpleTx creates a Bitcoin transaction paying to the specified
   254  	// outputs. The transaction is not broadcasted to the network. In the
   255  	// case the wallet has insufficient funds, or the outputs are
   256  	// non-standard, an error should be returned. This method also takes
   257  	// the target fee expressed in sat/kw that should be used when crafting
   258  	// the transaction.
   259  	//
   260  	// NOTE: The dryRun argument can be set true to create a tx that
   261  	// doesn't alter the database. A tx created with this set to true
   262  	// SHOULD NOT be broadcasted.
   263  	//
   264  	// NOTE: This method requires the global coin selection lock to be held.
   265  	CreateSimpleTx(outputs []*wire.TxOut, feeRate chainfee.AtomPerKByte,
   266  		minConfs int32, dryRun bool) (*txauthor.AuthoredTx, error)
   267  
   268  	// ListUnspentWitness returns all unspent outputs which are version 0
   269  	// witness programs. The 'minConfs' and 'maxConfs' parameters
   270  	// indicate the minimum and maximum number of confirmations an output
   271  	// needs in order to be returned by this method. Passing -1 as
   272  	// 'minConfs' indicates that even unconfirmed outputs should be
   273  	// returned. Using MaxInt32 as 'maxConfs' implies returning all
   274  	// outputs with at least 'minConfs'. The account parameter serves as
   275  	// a filter to retrieve the unspent outputs for a specific account.
   276  	// When empty, the unspent outputs of all wallet accounts are returned.
   277  	//
   278  	// NOTE: This method requires the global coin selection lock to be held.
   279  	ListUnspentWitness(minConfs, maxConfs int32,
   280  		accountFilter string) ([]*Utxo, error)
   281  
   282  	// ListTransactionDetails returns a list of all transactions which are
   283  	// relevant to the wallet over [startHeight;endHeight]. If start height
   284  	// is greater than end height, the transactions will be retrieved in
   285  	// reverse order. To include unconfirmed transactions, endHeight should
   286  	// be set to the special value -1. This will return transactions from
   287  	// the tip of the chain until the start height (inclusive) and
   288  	// unconfirmed transactions. The account parameter serves as a filter to
   289  	// retrieve the transactions relevant to a specific account. When
   290  	// empty, transactions of all wallet accounts are returned.
   291  	ListTransactionDetails(startHeight, endHeight int32,
   292  		accountFilter string) ([]*TransactionDetail, error)
   293  
   294  	// LockOutpoint marks an outpoint as locked meaning it will no longer
   295  	// be deemed as eligible for coin selection. Locking outputs are
   296  	// utilized in order to avoid race conditions when selecting inputs for
   297  	// usage when funding a channel.
   298  	//
   299  	// NOTE: This method requires the global coin selection lock to be held.
   300  	LockOutpoint(o wire.OutPoint)
   301  
   302  	// UnlockOutpoint unlocks a previously locked output, marking it
   303  	// eligible for coin selection.
   304  	//
   305  	// NOTE: This method requires the global coin selection lock to be held.
   306  	UnlockOutpoint(o wire.OutPoint)
   307  
   308  	// LeaseOutput locks an output to the given ID, preventing it from being
   309  	// available for any future coin selection attempts. The absolute time
   310  	// of the lock's expiration is returned. The expiration of the lock can
   311  	// be extended by successive invocations of this call. Outputs can be
   312  	// unlocked before their expiration through `ReleaseOutput`.
   313  	//
   314  	// If the output is not known, wtxmgr.ErrUnknownOutput is returned. If
   315  	// the output has already been locked to a different ID, then
   316  	// wtxmgr.ErrOutputAlreadyLocked is returned.
   317  	//
   318  	// NOTE: This method requires the global coin selection lock to be held.
   319  	LeaseOutput(id LockID, op wire.OutPoint,
   320  		duration time.Duration) (time.Time, error)
   321  
   322  	// ReleaseOutput unlocks an output, allowing it to be available for coin
   323  	// selection if it remains unspent. The ID should match the one used to
   324  	// originally lock the output.
   325  	//
   326  	// NOTE: This method requires the global coin selection lock to be held.
   327  	ReleaseOutput(id LockID, op wire.OutPoint) error
   328  
   329  	// ListLeasedOutputs returns a list of all currently locked outputs.
   330  	ListLeasedOutputs() ([]*LockedOutput, error)
   331  
   332  	// PublishTransaction performs cursory validation (dust checks, etc),
   333  	// then finally broadcasts the passed transaction to the Decred network.
   334  	// If the transaction is rejected because it is conflicting with an
   335  	// already known transaction, ErrDoubleSpend is returned. If the
   336  	// transaction is already known (published already), no error will be
   337  	// returned. Other error returned depends on the currently active chain
   338  	// backend. It takes an optional label which will save a label with the
   339  	// published transaction.
   340  	PublishTransaction(tx *wire.MsgTx, label string) error
   341  
   342  	// AbandonDoubleSpends removes all unconfirmed transactions that also
   343  	// spend any of the specified outpoints from the wallet. This is used
   344  	// to fix issues when an input used in multiple different sweep
   345  	// transactions gets confirmed in one of them (rendering the other
   346  	// transactions invalid).
   347  	AbandonDoubleSpends(spentOutpoints ...*wire.OutPoint) error
   348  
   349  	// LabelTransaction adds a label to a transaction. If the tx already
   350  	// has a label, this call will fail unless the overwrite parameter is
   351  	// set. Labels must not be empty, and they are limited to 500 chars.
   352  	LabelTransaction(hash chainhash.Hash, label string, overwrite bool) error
   353  
   354  	// FetchTx attempts to fetch a transaction in the wallet's database
   355  	// identified by the passed transaction hash. If the transaction can't
   356  	// be found, then a nil pointer is returned.
   357  	FetchTx(chainhash.Hash) (*wire.MsgTx, error)
   358  
   359  	// RemoveDescendants attempts to remove any transaction from the
   360  	// wallet's tx store (that may be unconfirmed) that spends outputs
   361  	// created by the passed transaction. This remove propagates
   362  	// recursively down the chain of descendent transactions.
   363  	RemoveDescendants(*wire.MsgTx) error
   364  
   365  	// FundPsbt creates a fully populated PSBT packet that contains enough
   366  	// inputs to fund the outputs specified in the passed in packet with the
   367  	// specified fee rate. If there is change left, a change output from the
   368  	// internal wallet is added and the index of the change output is
   369  	// returned. Otherwise no additional output is created and the index -1
   370  	// is returned.
   371  	//
   372  	// NOTE: If the packet doesn't contain any inputs, coin selection is
   373  	// performed automatically. The account parameter must be non-empty as
   374  	// it determines which set of coins are eligible for coin selection. If
   375  	// the packet does contain any inputs, it is assumed that full coin
   376  	// selection happened externally and no additional inputs are added. If
   377  	// the specified inputs aren't enough to fund the outputs with the given
   378  	// fee rate, an error is returned. No lock lease is acquired for any of
   379  	// the selected/validated inputs. It is in the caller's responsibility
   380  	// to lock the inputs before handing them out.
   381  	FundPsbt(packet *psbt.Packet, minConfs int32,
   382  		feeRate chainfee.AtomPerKByte, account string) (int32, error)
   383  
   384  	// SignPsbt expects a partial transaction with all inputs and outputs
   385  	// fully declared and tries to sign all unsigned inputs that have all
   386  	// required fields (UTXO information, BIP32 derivation information,
   387  	// witness or sig scripts) set.
   388  	// If no error is returned, the PSBT is ready to be given to the next
   389  	// signer or to be finalized if lnd was the last signer.
   390  	//
   391  	// NOTE: This method only signs inputs (and only those it can sign), it
   392  	// does not perform any other tasks (such as coin selection, UTXO
   393  	// locking or input/output/fee value validation, PSBT finalization). Any
   394  	// input that is incomplete will be skipped.
   395  	SignPsbt(packet *psbt.Packet) error
   396  
   397  	// FinalizePsbt expects a partial transaction with all inputs and
   398  	// outputs fully declared and tries to sign all inputs that belong to
   399  	// the wallet. Lnd must be the last signer of the transaction. That
   400  	// means, if there are any unsigned non-witness inputs or inputs without
   401  	// UTXO information attached or inputs without witness data that do not
   402  	// belong to lnd's wallet, this method will fail. If no error is
   403  	// returned, the PSBT is ready to be extracted and the final TX within
   404  	// to be broadcast.
   405  	//
   406  	// NOTE: This method does NOT publish the transaction after it's been
   407  	// finalized successfully.
   408  	FinalizePsbt(packet *psbt.Packet) error
   409  
   410  	// SubscribeTransactions returns a TransactionSubscription client which
   411  	// is capable of receiving async notifications as new transactions
   412  	// related to the wallet are seen within the network, or found in
   413  	// blocks.
   414  	//
   415  	// NOTE: a non-nil error should be returned if notifications aren't
   416  	// supported.
   417  	//
   418  	// TODO(roasbeef): make distinct interface?
   419  	SubscribeTransactions() (TransactionSubscription, error)
   420  
   421  	// IsSynced returns a boolean indicating if from the PoV of the wallet,
   422  	// it has fully synced to the current best block in the main chain.
   423  	// It also returns an int64 indicating the timestamp of the best block
   424  	// known to the wallet, expressed in Unix epoch time
   425  	IsSynced() (bool, int64, error)
   426  
   427  	// InitialSyncChannel returns a channel that is closed once the wallet
   428  	// has performed its initial sync procedures, is caught up to the
   429  	// network and potentially ready for use.
   430  	InitialSyncChannel() <-chan struct{}
   431  
   432  	// BestBlock returns the block height, block hash and timestamp for the
   433  	// tip of the main chain as viewed by the internal wallet controller.
   434  	BestBlock() (int64, chainhash.Hash, int64, error)
   435  
   436  	// GetRecoveryInfo returns a boolean indicating whether the wallet is
   437  	// started in recovery mode. It also returns a float64 indicating the
   438  	// recovery progress made so far.
   439  	GetRecoveryInfo() (bool, float64, error)
   440  
   441  	// Start initializes the wallet, making any necessary connections,
   442  	// starting up required goroutines etc.
   443  	Start() error
   444  
   445  	// Stop signals the wallet for shutdown. Shutdown may entail closing
   446  	// any active sockets, database handles, stopping goroutines, etc.
   447  	Stop() error
   448  
   449  	// BackEnd returns a name for the wallet's backing chain service,
   450  	// which could be e.g. dcrd or another consensus service.
   451  	BackEnd() string
   452  }
   453  
   454  // BlockChainIO is a dedicated source which will be used to obtain queries
   455  // related to the current state of the blockchain. The data returned by each of
   456  // the defined methods within this interface should always return the most up
   457  // to date data possible.
   458  //
   459  // TODO(roasbeef): move to diff package perhaps?
   460  // TODO(roasbeef): move publish txn here?
   461  type BlockChainIO interface {
   462  	// GetBestBlock returns the current height and block hash of the valid
   463  	// most-work chain the implementation is aware of.
   464  	GetBestBlock() (*chainhash.Hash, int32, error)
   465  
   466  	// GetUtxo attempts to return the passed outpoint if it's still a
   467  	// member of the utxo set. The passed height hint should be the "birth
   468  	// height" of the passed outpoint. The script passed should be the
   469  	// script that the outpoint creates. In the case that the output is in
   470  	// the UTXO set, then the output corresponding to that output is
   471  	// returned.  Otherwise, a non-nil error will be returned.
   472  	// As for some backends this call can initiate a rescan, the passed
   473  	// cancel channel can be closed to abort the call.
   474  	GetUtxo(op *wire.OutPoint, pkScript []byte, heightHint uint32,
   475  		cancel <-chan struct{}) (*wire.TxOut, error)
   476  
   477  	// GetBlockHash returns the hash of the block in the best blockchain
   478  	// at the given height.
   479  	GetBlockHash(blockHeight int64) (*chainhash.Hash, error)
   480  
   481  	// GetBlock returns the block in the main chain identified by the given
   482  	// hash.
   483  	GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error)
   484  }
   485  
   486  // Messageinput.Signer represents an abstract object capable of signing arbitrary
   487  // messages. The capabilities of this interface are used to sign announcements
   488  // to the network, or just arbitrary messages that leverage the wallet's keys
   489  // to attest to some message.
   490  type MessageSigner interface {
   491  	// SignMessage attempts to sign a target message with the private key
   492  	// described in the key locator. If the target private key is unable to
   493  	// be found, then an error will be returned. The actual digest signed is
   494  	// the chainhash of the passed message.
   495  	SignMessage(keyLoc keychain.KeyLocator, msg []byte,
   496  		doubleHash bool) (*ecdsa.Signature, error)
   497  }
   498  
   499  // WalletDriver represents a "driver" for a particular concrete
   500  // WalletController implementation. A driver is identified by a globally unique
   501  // string identifier along with a 'New()' method which is responsible for
   502  // initializing a particular WalletController concrete implementation.
   503  type WalletDriver struct {
   504  	// WalletType is a string which uniquely identifies the
   505  	// WalletController that this driver, drives.
   506  	WalletType string
   507  
   508  	// New creates a new instance of a concrete WalletController
   509  	// implementation given a variadic set up arguments. The function takes
   510  	// a variadic number of interface parameters in order to provide
   511  	// initialization flexibility, thereby accommodating several potential
   512  	// WalletController implementations.
   513  	New func(args ...interface{}) (WalletController, error)
   514  
   515  	// BackEnds returns a list of available chain service drivers for the
   516  	// wallet driver.
   517  	BackEnds func() []string
   518  }
   519  
   520  var (
   521  	wallets     = make(map[string]*WalletDriver)
   522  	registerMtx sync.Mutex
   523  )
   524  
   525  // RegisteredWallets returns a slice of all currently registered notifiers.
   526  //
   527  // NOTE: This function is safe for concurrent access.
   528  func RegisteredWallets() []*WalletDriver {
   529  	registerMtx.Lock()
   530  	defer registerMtx.Unlock()
   531  
   532  	registeredWallets := make([]*WalletDriver, 0, len(wallets))
   533  	for _, wallet := range wallets {
   534  		registeredWallets = append(registeredWallets, wallet)
   535  	}
   536  
   537  	return registeredWallets
   538  }
   539  
   540  // RegisterWallet registers a WalletDriver which is capable of driving a
   541  // concrete WalletController interface. In the case that this driver has
   542  // already been registered, an error is returned.
   543  //
   544  // NOTE: This function is safe for concurrent access.
   545  func RegisterWallet(driver *WalletDriver) error {
   546  	registerMtx.Lock()
   547  	defer registerMtx.Unlock()
   548  
   549  	if _, ok := wallets[driver.WalletType]; ok {
   550  		return fmt.Errorf("wallet already registered")
   551  	}
   552  
   553  	wallets[driver.WalletType] = driver
   554  
   555  	return nil
   556  }
   557  
   558  // SupportedWallets returns a slice of strings that represents the wallet
   559  // drivers that have been registered and are therefore supported.
   560  //
   561  // NOTE: This function is safe for concurrent access.
   562  func SupportedWallets() []string {
   563  	registerMtx.Lock()
   564  	defer registerMtx.Unlock()
   565  
   566  	supportedWallets := make([]string, 0, len(wallets))
   567  	for walletName := range wallets {
   568  		supportedWallets = append(supportedWallets, walletName)
   569  	}
   570  
   571  	return supportedWallets
   572  }
   573  
   574  // WalletDriverForName returns the wallet driver for the given wallet type name.
   575  // Returns nil if the wallet driver does not exist.
   576  func WalletDriverForName(name string) *WalletDriver {
   577  	registerMtx.Lock()
   578  	defer registerMtx.Unlock()
   579  
   580  	for walletName, driver := range wallets {
   581  		if walletName == name {
   582  			return driver
   583  		}
   584  	}
   585  
   586  	return nil
   587  
   588  }