decred.org/dcrdex@v1.0.3/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 } 49 50 type tipRedemptionWallet interface { 51 Wallet 52 getBlockHeight(*chainhash.Hash) (int32, error) 53 getBlockHeader(blockHash *chainhash.Hash) (hdr *BlockHeader, mainchain bool, err error) 54 getBlock(h chainhash.Hash) (*wire.MsgBlock, error) 55 searchBlockForRedemptions(ctx context.Context, reqs map[OutPoint]*FindRedemptionReq, blockHash chainhash.Hash) (discovered map[OutPoint]*FindRedemptionResult) 56 findRedemptionsInMempool(ctx context.Context, reqs map[OutPoint]*FindRedemptionReq) (discovered map[OutPoint]*FindRedemptionResult) 57 } 58 59 type txFeeEstimator interface { 60 estimateSendTxFee(tx *wire.MsgTx, feeRate uint64, subtract bool) (fee uint64, err error) 61 } 62 63 // walletTxChecker provide a fast wallet tx query when block info not needed. 64 // This should be treated as an optimization method, where getWalletTransaction 65 // may always be used in its place if it does not exists. 66 type walletTxChecker interface { 67 checkWalletTx(txid string) ([]byte, uint32, error) 68 } 69 70 // tipNotifier can be implemented if the Wallet is able to provide a stream of 71 // blocks as they are finished being processed. 72 type tipNotifier interface { 73 tipFeed() <-chan *BlockVector 74 } 75 76 const medianTimeBlocks = 11 77 78 // chainStamper is a source of the timestamp and the previous block hash for a 79 // specified block. A chainStamper is used to manually calculate the median time 80 // for a block. 81 type chainStamper func(*chainhash.Hash) (stamp time.Time, prevHash *chainhash.Hash, err error) 82 83 // CalcMedianTime calculates the median time of the previous 11 block headers. 84 // The median time is used for validating time-locked transactions. See notes in 85 // btcd/blockchain (*blockNode).CalcPastMedianTime() regarding incorrectly 86 // calculated median time for blocks 1, 3, 5, 7, and 9. 87 func CalcMedianTime(stamper chainStamper, blockHash *chainhash.Hash) (time.Time, error) { 88 timestamps := make([]int64, 0, medianTimeBlocks) 89 90 zeroHash := chainhash.Hash{} 91 92 h := blockHash 93 for i := 0; i < medianTimeBlocks; i++ { 94 stamp, prevHash, err := stamper(h) 95 if err != nil { 96 return time.Time{}, fmt.Errorf("BlockHeader error for hash %q: %v", h, err) 97 } 98 timestamps = append(timestamps, stamp.Unix()) 99 100 if *prevHash == zeroHash { 101 break 102 } 103 h = prevHash 104 } 105 106 sort.Slice(timestamps, func(i, j int) bool { 107 return timestamps[i] < timestamps[j] 108 }) 109 110 medianTimestamp := timestamps[len(timestamps)/2] 111 return time.Unix(medianTimestamp, 0), nil 112 }