func NewFeeEstimator(maxRollback, minRegisteredBlocks uint32) *FeeEstimator {
	return &FeeEstimator{
		maxRollback:         maxRollback,
		minRegisteredBlocks: minRegisteredBlocks,
		lastKnownHeight:     miningdag.UnminedHeight,
		binSize:             estimateFeeBinSize,
		maxReplacements:     estimateFeeMaxReplacements,
		observed:            make(map[chainhash.Hash]*observedTransaction),
		dropped:             make([]*registeredBlock, 0, maxRollback),
	}
}
NewFeeEstimator creates a FeeEstimator for which at most maxRollback blocks
can be unregistered and which returns an error unless minRegisteredBlocks
have been registered with it.
package mempool
import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"math"
	"math/rand"
	"sort"
	"strings"
	"sync"
	"github.com/soteria-dag/soterd/chaincfg/chainhash"
	"github.com/soteria-dag/soterd/miningdag"
	"github.com/soteria-dag/soterd/soterutil"
)
const (
	estimateFeeDepth = 25
	estimateFeeBinSize = 100
	estimateFeeMaxReplacements = 10
	DefaultEstimateFeeMaxRollback = 2
	DefaultEstimateFeeMinRegisteredBlocks = 3
	bytePerKb = 1000
)
var (
	EstimateFeeDatabaseKey = []byte("estimatefee")
)
type nanoSoterPerByte float64
type SotoPerKilobyte float64
func (rate nanoSoterPerByte) ToSotoPerKb() SotoPerKilobyte {
	if rate == nanoSoterPerByte(-1.0) {
		return -1.0
	}
	return SotoPerKilobyte(float64(rate) * bytePerKb * soterutil.NanoSoter)
}
func (rate nanoSoterPerByte) Fee(size uint32) soterutil.Amount {
	if rate == nanoSoterPerByte(-1) {
		return soterutil.Amount(-1)
	}
	return soterutil.Amount(float64(rate) * float64(size))
}
func NewNanoSoterPerByte(fee soterutil.Amount, size uint32) nanoSoterPerByte {
	return nanoSoterPerByte(float64(fee) / float64(size))
}
type observedTransaction struct {
	hash chainhash.Hash
	feeRate nanoSoterPerByte
	observed int32
	mined int32
}
func (o *observedTransaction) Serialize(w io.Writer) {
	binary.Write(w, binary.BigEndian, o.hash)
	binary.Write(w, binary.BigEndian, o.feeRate)
	binary.Write(w, binary.BigEndian, o.observed)
	binary.Write(w, binary.BigEndian, o.mined)
}
func deserializeObservedTransaction(r io.Reader) (*observedTransaction, error) {
	ot := observedTransaction{}
	binary.Read(r, binary.BigEndian, &ot.hash)
	binary.Read(r, binary.BigEndian, &ot.feeRate)
	binary.Read(r, binary.BigEndian, &ot.observed)
	binary.Read(r, binary.BigEndian, &ot.mined)
	return &ot, nil
}
type registeredBlock struct {
	hash         chainhash.Hash
	transactions []*observedTransaction
}
func (rb *registeredBlock) serialize(w io.Writer, txs map[*observedTransaction]uint32) {
	binary.Write(w, binary.BigEndian, rb.hash)
	binary.Write(w, binary.BigEndian, uint32(len(rb.transactions)))
	for _, o := range rb.transactions {
		binary.Write(w, binary.BigEndian, txs[o])
	}
}
type FeeEstimator struct {
	maxRollback uint32
	binSize     int32
	maxReplacements int32
	minRegisteredBlocks uint32
	lastKnownHeight int32
	numBlocksRegistered uint32
	mtx      sync.RWMutex
	observed map[chainhash.Hash]*observedTransaction
	bin      [estimateFeeDepth][]*observedTransaction
	cached []nanoSoterPerByte
	dropped []*registeredBlock
}