Implement alternative estimateFee using whatthefee.io WIP #153
This commit is contained in:
parent
942c95add8
commit
55e39f0ea4
@ -36,25 +36,27 @@ type BitcoinRPC struct {
|
||||
|
||||
// Configuration represents json config file
|
||||
type Configuration struct {
|
||||
CoinName string `json:"coin_name"`
|
||||
CoinShortcut string `json:"coin_shortcut"`
|
||||
RPCURL string `json:"rpc_url"`
|
||||
RPCUser string `json:"rpc_user"`
|
||||
RPCPass string `json:"rpc_pass"`
|
||||
RPCTimeout int `json:"rpc_timeout"`
|
||||
Parse bool `json:"parse"`
|
||||
MessageQueueBinding string `json:"message_queue_binding"`
|
||||
Subversion string `json:"subversion"`
|
||||
BlockAddressesToKeep int `json:"block_addresses_to_keep"`
|
||||
MempoolWorkers int `json:"mempool_workers"`
|
||||
MempoolSubWorkers int `json:"mempool_sub_workers"`
|
||||
AddressFormat string `json:"address_format"`
|
||||
SupportsEstimateFee bool `json:"supports_estimate_fee"`
|
||||
SupportsEstimateSmartFee bool `json:"supports_estimate_smart_fee"`
|
||||
XPubMagic uint32 `json:"xpub_magic,omitempty"`
|
||||
XPubMagicSegwitP2sh uint32 `json:"xpub_magic_segwit_p2sh,omitempty"`
|
||||
XPubMagicSegwitNative uint32 `json:"xpub_magic_segwit_native,omitempty"`
|
||||
Slip44 uint32 `json:"slip44,omitempty"`
|
||||
CoinName string `json:"coin_name"`
|
||||
CoinShortcut string `json:"coin_shortcut"`
|
||||
RPCURL string `json:"rpc_url"`
|
||||
RPCUser string `json:"rpc_user"`
|
||||
RPCPass string `json:"rpc_pass"`
|
||||
RPCTimeout int `json:"rpc_timeout"`
|
||||
Parse bool `json:"parse"`
|
||||
MessageQueueBinding string `json:"message_queue_binding"`
|
||||
Subversion string `json:"subversion"`
|
||||
BlockAddressesToKeep int `json:"block_addresses_to_keep"`
|
||||
MempoolWorkers int `json:"mempool_workers"`
|
||||
MempoolSubWorkers int `json:"mempool_sub_workers"`
|
||||
AddressFormat string `json:"address_format"`
|
||||
SupportsEstimateFee bool `json:"supports_estimate_fee"`
|
||||
SupportsEstimateSmartFee bool `json:"supports_estimate_smart_fee"`
|
||||
XPubMagic uint32 `json:"xpub_magic,omitempty"`
|
||||
XPubMagicSegwitP2sh uint32 `json:"xpub_magic_segwit_p2sh,omitempty"`
|
||||
XPubMagicSegwitNative uint32 `json:"xpub_magic_segwit_native,omitempty"`
|
||||
Slip44 uint32 `json:"slip44,omitempty"`
|
||||
AlternativeEstimateFee string `json:"alternativeEstimateFee,omitempty"`
|
||||
AlternativeEstimateFeeParams string `json:"alternativeEstimateFeeParams,omitempty"`
|
||||
}
|
||||
|
||||
// NewBitcoinRPC returns new BitcoinRPC instance.
|
||||
@ -127,6 +129,14 @@ func (b *BitcoinRPC) Initialize() error {
|
||||
|
||||
glog.Info("rpc: block chain ", params.Name)
|
||||
|
||||
if b.ChainConfig.AlternativeEstimateFee == "whatthefee" {
|
||||
if err = InitWhatTheFee(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil {
|
||||
glog.Error("InitWhatTheFee error ", err, " Reverting to default estimateFee functionality")
|
||||
// disable AlternativeEstimateFee logic
|
||||
b.ChainConfig.AlternativeEstimateFee = ""
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
129
bchain/coins/btc/whatthefee.go
Normal file
129
bchain/coins/btc/whatthefee.go
Normal file
@ -0,0 +1,129 @@
|
||||
package btc
|
||||
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/juju/errors"
|
||||
)
|
||||
|
||||
// https://whatthefee.io returns
|
||||
// {"index": [3, 6, 9, 12, 18, 24, 36, 48, 72, 96, 144],
|
||||
// "columns": ["0.0500", "0.2000", "0.5000", "0.8000", "0.9500"],
|
||||
// "data": [[60, 180, 280, 400, 440], [20, 120, 180, 380, 440],
|
||||
// [0, 120, 160, 360, 420], [0, 80, 160, 300, 380], [0, 20, 120, 220, 360],
|
||||
// [0, 20, 100, 180, 300], [0, 0, 80, 140, 240], [0, 0, 60, 100, 180],
|
||||
// [0, 0, 40, 60, 140], [0, 0, 20, 20, 60], [0, 0, 0, 0, 20]]}
|
||||
|
||||
type whatTheFeeServiceResult struct {
|
||||
Index []int `json:"index"`
|
||||
Columns []string `json:"columns"`
|
||||
Data [][]int `json:"data"`
|
||||
}
|
||||
|
||||
type whatTheFeeParams struct {
|
||||
URL string `json:"url"`
|
||||
PeriodSeconds int `periodSeconds:"url"`
|
||||
}
|
||||
|
||||
type whatTheFeeFee struct {
|
||||
blocks int
|
||||
feesPerKB []int
|
||||
}
|
||||
|
||||
type whatTheFeeData struct {
|
||||
params whatTheFeeParams
|
||||
probabilities []string
|
||||
fees []whatTheFeeFee
|
||||
lastSync time.Time
|
||||
chain bchain.BlockChain
|
||||
mux sync.Mutex
|
||||
}
|
||||
|
||||
var whatTheFee whatTheFeeData
|
||||
|
||||
// InitWhatTheFee initializes https://whatthefee.io handler
|
||||
func InitWhatTheFee(chain bchain.BlockChain, params string) error {
|
||||
err := json.Unmarshal([]byte(params), &whatTheFee.params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if whatTheFee.params.URL == "" || whatTheFee.params.PeriodSeconds == 0 {
|
||||
return errors.New("Missing parameters")
|
||||
}
|
||||
whatTheFee.chain = chain
|
||||
go whatTheFeeDownloader()
|
||||
return nil
|
||||
}
|
||||
|
||||
func whatTheFeeDownloader() {
|
||||
period := time.Duration(whatTheFee.params.PeriodSeconds) * time.Second
|
||||
timer := time.NewTimer(period)
|
||||
for {
|
||||
var data whatTheFeeServiceResult
|
||||
err := whatTheFeeGetData(&data)
|
||||
if err != nil {
|
||||
glog.Error("whatTheFeeGetData ", err)
|
||||
} else {
|
||||
whatTheFeeProcessData(&data)
|
||||
}
|
||||
<-timer.C
|
||||
timer.Reset(period)
|
||||
}
|
||||
}
|
||||
|
||||
func whatTheFeeProcessData(data *whatTheFeeServiceResult) {
|
||||
if len(data.Index) == 0 || len(data.Index) != len(data.Data) || len(data.Columns) == 0 {
|
||||
glog.Errorf("invalid data %+v", data)
|
||||
return
|
||||
}
|
||||
whatTheFee.mux.Lock()
|
||||
defer whatTheFee.mux.Unlock()
|
||||
whatTheFee.probabilities = data.Columns
|
||||
whatTheFee.fees = make([]whatTheFeeFee, len(data.Index))
|
||||
for i, blocks := range data.Index {
|
||||
if len(data.Columns) != len(data.Data[i]) {
|
||||
glog.Errorf("invalid data %+v", data)
|
||||
return
|
||||
}
|
||||
fees := make([]int, len(data.Columns))
|
||||
for j, l := range data.Data[i] {
|
||||
fees[j] = int(1000 * math.Exp(float64(l)/100))
|
||||
}
|
||||
whatTheFee.fees[i] = whatTheFeeFee{
|
||||
blocks: blocks,
|
||||
feesPerKB: fees,
|
||||
}
|
||||
}
|
||||
whatTheFee.lastSync = time.Now()
|
||||
glog.Infof("%+v", whatTheFee.fees)
|
||||
}
|
||||
|
||||
func whatTheFeeGetData(res interface{}) error {
|
||||
var httpData []byte
|
||||
httpReq, err := http.NewRequest("GET", whatTheFee.params.URL, bytes.NewBuffer(httpData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpRes, err := http.DefaultClient.Do(httpReq)
|
||||
if httpRes != nil {
|
||||
defer httpRes.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if server returns HTTP error code it might not return json with response
|
||||
// handle both cases
|
||||
if httpRes.StatusCode != 200 {
|
||||
return errors.New("whatthefee.io returned status " + strconv.Itoa(httpRes.StatusCode))
|
||||
}
|
||||
return safeDecodeResponse(httpRes.Body, &res)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user