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