github.com/cryptohub-digital/blockbook-fork@v0.0.0-20230713133354-673c927af7f1/bchain/coins/btc/whatthefee.go (about) 1 package btc 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "math" 8 "net/http" 9 "strconv" 10 "sync" 11 "time" 12 13 "github.com/cryptohub-digital/blockbook-fork/bchain" 14 "github.com/golang/glog" 15 "github.com/juju/errors" 16 ) 17 18 // https://whatthefee.io returns 19 // {"index": [3, 6, 9, 12, 18, 24, 36, 48, 72, 96, 144], 20 // "columns": ["0.0500", "0.2000", "0.5000", "0.8000", "0.9500"], 21 // "data": [[60, 180, 280, 400, 440], [20, 120, 180, 380, 440], 22 // [0, 120, 160, 360, 420], [0, 80, 160, 300, 380], [0, 20, 120, 220, 360], 23 // [0, 20, 100, 180, 300], [0, 0, 80, 140, 240], [0, 0, 60, 100, 180], 24 // [0, 0, 40, 60, 140], [0, 0, 20, 20, 60], [0, 0, 0, 0, 20]]} 25 26 type whatTheFeeServiceResult struct { 27 Index []int `json:"index"` 28 Columns []string `json:"columns"` 29 Data [][]int `json:"data"` 30 } 31 32 type whatTheFeeParams struct { 33 URL string `json:"url"` 34 PeriodSeconds int `periodSeconds:"url"` 35 } 36 37 type whatTheFeeFee struct { 38 blocks int 39 feesPerKB []int 40 } 41 42 type whatTheFeeData struct { 43 params whatTheFeeParams 44 probabilities []string 45 fees []whatTheFeeFee 46 lastSync time.Time 47 chain bchain.BlockChain 48 mux sync.Mutex 49 } 50 51 var whatTheFee whatTheFeeData 52 53 // InitWhatTheFee initializes https://whatthefee.io handler 54 func InitWhatTheFee(chain bchain.BlockChain, params string) error { 55 err := json.Unmarshal([]byte(params), &whatTheFee.params) 56 if err != nil { 57 return err 58 } 59 if whatTheFee.params.URL == "" || whatTheFee.params.PeriodSeconds == 0 { 60 return errors.New("Missing parameters") 61 } 62 whatTheFee.chain = chain 63 go whatTheFeeDownloader() 64 return nil 65 } 66 67 func whatTheFeeDownloader() { 68 period := time.Duration(whatTheFee.params.PeriodSeconds) * time.Second 69 timer := time.NewTimer(period) 70 counter := 0 71 for { 72 var data whatTheFeeServiceResult 73 err := whatTheFeeGetData(&data) 74 if err != nil { 75 glog.Error("whatTheFeeGetData ", err) 76 } else { 77 if whatTheFeeProcessData(&data) { 78 if counter%60 == 0 { 79 whatTheFeeCompareToDefault() 80 } 81 counter++ 82 } 83 } 84 <-timer.C 85 timer.Reset(period) 86 } 87 } 88 89 func whatTheFeeProcessData(data *whatTheFeeServiceResult) bool { 90 if len(data.Index) == 0 || len(data.Index) != len(data.Data) || len(data.Columns) == 0 { 91 glog.Errorf("invalid data %+v", data) 92 return false 93 } 94 whatTheFee.mux.Lock() 95 defer whatTheFee.mux.Unlock() 96 whatTheFee.probabilities = data.Columns 97 whatTheFee.fees = make([]whatTheFeeFee, len(data.Index)) 98 for i, blocks := range data.Index { 99 if len(data.Columns) != len(data.Data[i]) { 100 glog.Errorf("invalid data %+v", data) 101 return false 102 } 103 fees := make([]int, len(data.Columns)) 104 for j, l := range data.Data[i] { 105 fees[j] = int(1000 * math.Exp(float64(l)/100)) 106 } 107 whatTheFee.fees[i] = whatTheFeeFee{ 108 blocks: blocks, 109 feesPerKB: fees, 110 } 111 } 112 whatTheFee.lastSync = time.Now() 113 glog.Infof("%+v", whatTheFee.fees) 114 return true 115 } 116 117 func whatTheFeeGetData(res interface{}) error { 118 var httpData []byte 119 httpReq, err := http.NewRequest("GET", whatTheFee.params.URL, bytes.NewBuffer(httpData)) 120 if err != nil { 121 return err 122 } 123 httpRes, err := http.DefaultClient.Do(httpReq) 124 if httpRes != nil { 125 defer httpRes.Body.Close() 126 } 127 if err != nil { 128 return err 129 } 130 if httpRes.StatusCode != 200 { 131 return errors.New("whatthefee.io returned status " + strconv.Itoa(httpRes.StatusCode)) 132 } 133 return safeDecodeResponse(httpRes.Body, &res) 134 } 135 136 func whatTheFeeCompareToDefault() { 137 output := "" 138 for _, fee := range whatTheFee.fees { 139 output += fmt.Sprint(fee.blocks, ",") 140 for _, wtf := range fee.feesPerKB { 141 output += fmt.Sprint(wtf, ",") 142 } 143 conservative, err := whatTheFee.chain.EstimateSmartFee(fee.blocks, true) 144 if err != nil { 145 glog.Error(err) 146 return 147 } 148 economical, err := whatTheFee.chain.EstimateSmartFee(fee.blocks, false) 149 if err != nil { 150 glog.Error(err) 151 return 152 } 153 output += fmt.Sprint(conservative.String(), ",", economical.String(), "\n") 154 } 155 glog.Info("whatTheFeeCompareToDefault\n", output) 156 }