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 }