github.com/trezor/blockbook@v0.4.1-0.20240328132726-e9a08582ee2c/bchain/coins/btc/mempoolspace.go (about) 1 package btc 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "net/http" 7 "strconv" 8 "time" 9 10 "github.com/golang/glog" 11 "github.com/juju/errors" 12 "github.com/trezor/blockbook/bchain" 13 ) 14 15 // https://mempool.space/api/v1/fees/recommended returns 16 // {"fastestFee":41,"halfHourFee":39,"hourFee":36,"economyFee":36,"minimumFee":20} 17 18 type mempoolSpaceFeeResult struct { 19 FastestFee int `json:"fastestFee"` 20 HalfHourFee int `json:"halfHourFee"` 21 HourFee int `json:"hourFee"` 22 EconomyFee int `json:"economyFee"` 23 MinimumFee int `json:"minimumFee"` 24 } 25 26 type mempoolSpaceFeeParams struct { 27 URL string `json:"url"` 28 PeriodSeconds int `periodSeconds:"url"` 29 } 30 31 type mempoolSpaceFeeProvider struct { 32 *alternativeFeeProvider 33 params mempoolSpaceFeeParams 34 } 35 36 // NewMempoolSpaceFee initializes https://mempool.space provider 37 func NewMempoolSpaceFee(chain bchain.BlockChain, params string) (alternativeFeeProviderInterface, error) { 38 p := &mempoolSpaceFeeProvider{alternativeFeeProvider: &alternativeFeeProvider{}} 39 err := json.Unmarshal([]byte(params), &p.params) 40 if err != nil { 41 return nil, err 42 } 43 if p.params.URL == "" || p.params.PeriodSeconds == 0 { 44 return nil, errors.New("NewWhatTheFee: Missing parameters") 45 } 46 p.chain = chain 47 go p.mempoolSpaceFeeDownloader() 48 return p, nil 49 } 50 51 func (p *mempoolSpaceFeeProvider) mempoolSpaceFeeDownloader() { 52 period := time.Duration(p.params.PeriodSeconds) * time.Second 53 timer := time.NewTimer(period) 54 counter := 0 55 for { 56 var data mempoolSpaceFeeResult 57 err := p.mempoolSpaceFeeGetData(&data) 58 if err != nil { 59 glog.Error("mempoolSpaceFeeGetData ", err) 60 } else { 61 if p.mempoolSpaceFeeProcessData(&data) { 62 if counter%60 == 0 { 63 p.compareToDefault() 64 } 65 counter++ 66 } 67 } 68 <-timer.C 69 timer.Reset(period) 70 } 71 } 72 73 func (p *mempoolSpaceFeeProvider) mempoolSpaceFeeProcessData(data *mempoolSpaceFeeResult) bool { 74 if data.MinimumFee == 0 || data.EconomyFee == 0 || data.HourFee == 0 || data.HalfHourFee == 0 || data.FastestFee == 0 { 75 glog.Errorf("mempoolSpaceFeeProcessData: invalid data %+v", data) 76 return false 77 } 78 p.mux.Lock() 79 defer p.mux.Unlock() 80 p.fees = make([]alternativeFeeProviderFee, 5) 81 // map mempoool.space fees to blocks 82 83 // FastestFee is for 1 block 84 p.fees[0] = alternativeFeeProviderFee{ 85 blocks: 1, 86 feePerKB: data.FastestFee * 1000, 87 } 88 89 // HalfHourFee is for 2-6 blocks 90 p.fees[1] = alternativeFeeProviderFee{ 91 blocks: 6, 92 feePerKB: data.HalfHourFee * 1000, 93 } 94 95 // HourFee is for 7-36 blocks 96 p.fees[2] = alternativeFeeProviderFee{ 97 blocks: 36, 98 feePerKB: data.HourFee * 1000, 99 } 100 101 // EconomyFee is for 37-200 blocks 102 p.fees[3] = alternativeFeeProviderFee{ 103 blocks: 500, 104 feePerKB: data.EconomyFee * 1000, 105 } 106 107 // MinimumFee is for over 500 blocks 108 p.fees[4] = alternativeFeeProviderFee{ 109 blocks: 1000, 110 feePerKB: data.MinimumFee * 1000, 111 } 112 113 p.lastSync = time.Now() 114 // glog.Infof("mempoolSpaceFees: %+v", p.fees) 115 return true 116 } 117 118 func (p *mempoolSpaceFeeProvider) mempoolSpaceFeeGetData(res interface{}) error { 119 var httpData []byte 120 httpReq, err := http.NewRequest("GET", p.params.URL, bytes.NewBuffer(httpData)) 121 if err != nil { 122 return err 123 } 124 httpRes, err := http.DefaultClient.Do(httpReq) 125 if httpRes != nil { 126 defer httpRes.Body.Close() 127 } 128 if err != nil { 129 return err 130 } 131 if httpRes.StatusCode != http.StatusOK { 132 return errors.New(p.params.URL + " returned status " + strconv.Itoa(httpRes.StatusCode)) 133 } 134 return safeDecodeResponse(httpRes.Body, &res) 135 }