decred.org/dcrdex@v1.0.5/client/asset/btc/wallet.go (about)

     1  package btc
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sort"
     7  	"sync"
     8  	"time"
     9  
    10  	"decred.org/dcrdex/client/asset"
    11  	"github.com/btcsuite/btcd/btcec/v2"
    12  	"github.com/btcsuite/btcd/btcutil"
    13  	"github.com/btcsuite/btcd/chaincfg/chainhash"
    14  	"github.com/btcsuite/btcd/wire"
    15  )
    16  
    17  // Wallet is the interface that BTC wallet backends must implement. TODO: plumb
    18  // all requests with a context.Context.
    19  type Wallet interface {
    20  	RawRequester // for localFeeRate/rpcFeeRate calls
    21  	Connect(ctx context.Context, wg *sync.WaitGroup) error
    22  	SendRawTransaction(tx *wire.MsgTx) (*chainhash.Hash, error)
    23  	GetTxOut(txHash *chainhash.Hash, index uint32, pkScript []byte, startTime time.Time) (*wire.TxOut, uint32, error)
    24  	GetBlockHash(blockHeight int64) (*chainhash.Hash, error)
    25  	GetBestBlockHash() (*chainhash.Hash, error)
    26  	GetBestBlockHeight() (int32, error)
    27  	MedianTime() (time.Time, error)
    28  	Balances() (*GetBalancesResult, error)
    29  	ListUnspent() ([]*ListUnspentResult, error) // must not return locked coins
    30  	LockUnspent(unlock bool, ops []*Output) error
    31  	ListLockUnspent() ([]*RPCOutpoint, error)
    32  	ChangeAddress() (btcutil.Address, error) // warning: don't just use the Stringer if there's a "recode" function for a clone e.g. BCH
    33  	ExternalAddress() (btcutil.Address, error)
    34  	SignTx(inTx *wire.MsgTx) (*wire.MsgTx, error)
    35  	PrivKeyForAddress(addr string) (*btcec.PrivateKey, error)
    36  	WalletUnlock(pw []byte) error
    37  	WalletLock() error
    38  	Locked() bool
    39  	SyncStatus() (*asset.SyncStatus, error)
    40  	PeerCount() (uint32, error)
    41  	SwapConfirmations(txHash *chainhash.Hash, vout uint32, contract []byte, startTime time.Time) (confs uint32, spent bool, err error)
    42  	GetBestBlockHeader() (*BlockHeader, error)
    43  	OwnsAddress(addr btcutil.Address) (bool, error) // this should probably just take a string
    44  	GetWalletTransaction(txHash *chainhash.Hash) (*GetTransactionResult, error)
    45  	Reconfigure(walletCfg *asset.WalletConfig, currentAddress string) (restartRequired bool, err error)
    46  	Fingerprint() (string, error)
    47  	ListTransactionsSinceBlock(blockHeight int32) ([]*ListTransactionsResult, error)
    48  	AddressUsed(addr string) (bool, error)
    49  }
    50  
    51  type TipRedemptionWallet interface {
    52  	Wallet
    53  	GetBlockHeight(*chainhash.Hash) (int32, error)
    54  	GetBlockHeader(blockHash *chainhash.Hash) (hdr *BlockHeader, mainchain bool, err error)
    55  	GetBlock(h chainhash.Hash) (*wire.MsgBlock, error)
    56  	SearchBlockForRedemptions(ctx context.Context, reqs map[OutPoint]*FindRedemptionReq, blockHash chainhash.Hash) (discovered map[OutPoint]*FindRedemptionResult)
    57  	FindRedemptionsInMempool(ctx context.Context, reqs map[OutPoint]*FindRedemptionReq) (discovered map[OutPoint]*FindRedemptionResult)
    58  }
    59  
    60  type TxFeeEstimator interface {
    61  	EstimateSendTxFee(tx *wire.MsgTx, feeRate uint64, subtract bool) (fee uint64, err error)
    62  }
    63  
    64  // walletTxChecker provide a fast wallet tx query when block info not needed.
    65  // This should be treated as an optimization method, where getWalletTransaction
    66  // may always be used in its place if it does not exists.
    67  type walletTxChecker interface {
    68  	checkWalletTx(txid string) ([]byte, uint32, error)
    69  }
    70  
    71  // tipNotifier can be implemented if the Wallet is able to provide a stream of
    72  // blocks as they are finished being processed.
    73  type tipNotifier interface {
    74  	tipFeed() <-chan *BlockVector
    75  }
    76  
    77  const medianTimeBlocks = 11
    78  
    79  // chainStamper is a source of the timestamp and the previous block hash for a
    80  // specified block. A chainStamper is used to manually calculate the median time
    81  // for a block.
    82  type chainStamper func(*chainhash.Hash) (stamp time.Time, prevHash *chainhash.Hash, err error)
    83  
    84  // CalcMedianTime calculates the median time of the previous 11 block headers.
    85  // The median time is used for validating time-locked transactions. See notes in
    86  // btcd/blockchain (*blockNode).CalcPastMedianTime() regarding incorrectly
    87  // calculated median time for blocks 1, 3, 5, 7, and 9.
    88  func CalcMedianTime(stamper chainStamper, blockHash *chainhash.Hash) (time.Time, error) {
    89  	timestamps := make([]int64, 0, medianTimeBlocks)
    90  
    91  	zeroHash := chainhash.Hash{}
    92  
    93  	h := blockHash
    94  	for i := 0; i < medianTimeBlocks; i++ {
    95  		stamp, prevHash, err := stamper(h)
    96  		if err != nil {
    97  			return time.Time{}, fmt.Errorf("BlockHeader error for hash %q: %v", h, err)
    98  		}
    99  		timestamps = append(timestamps, stamp.Unix())
   100  
   101  		if *prevHash == zeroHash {
   102  			break
   103  		}
   104  		h = prevHash
   105  	}
   106  
   107  	sort.Slice(timestamps, func(i, j int) bool {
   108  		return timestamps[i] < timestamps[j]
   109  	})
   110  
   111  	medianTimestamp := timestamps[len(timestamps)/2]
   112  	return time.Unix(medianTimestamp, 0), nil
   113  }