github.com/lbryio/lbcd@v0.22.119/mempool/estimatefee.go (about)

     1  // Copyright (c) 2016 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package mempool
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"math"
    14  	"math/rand"
    15  	"sort"
    16  	"strings"
    17  	"sync"
    18  
    19  	"github.com/lbryio/lbcd/chaincfg/chainhash"
    20  	"github.com/lbryio/lbcd/mining"
    21  	btcutil "github.com/lbryio/lbcutil"
    22  )
    23  
    24  // TODO incorporate Alex Morcos' modifications to Gavin's initial model
    25  // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2014-October/006824.html
    26  
    27  const (
    28  	// estimateFeeDepth is the maximum number of blocks before a transaction
    29  	// is confirmed that we want to track.
    30  	estimateFeeDepth = 25
    31  
    32  	// estimateFeeBinSize is the number of txs stored in each bin.
    33  	estimateFeeBinSize = 100
    34  
    35  	// estimateFeeMaxReplacements is the max number of replacements that
    36  	// can be made by the txs found in a given block.
    37  	estimateFeeMaxReplacements = 10
    38  
    39  	// DefaultEstimateFeeMaxRollback is the default number of rollbacks
    40  	// allowed by the fee estimator for orphaned blocks.
    41  	DefaultEstimateFeeMaxRollback = 2
    42  
    43  	// DefaultEstimateFeeMinRegisteredBlocks is the default minimum
    44  	// number of blocks which must be observed by the fee estimator before
    45  	// it will provide fee estimations.
    46  	DefaultEstimateFeeMinRegisteredBlocks = 3
    47  
    48  	bytePerKb = 1000
    49  
    50  	btcPerSatoshi = 1e-8
    51  )
    52  
    53  var (
    54  	// EstimateFeeDatabaseKey is the key that we use to
    55  	// store the fee estimator in the database.
    56  	EstimateFeeDatabaseKey = []byte("estimatefee")
    57  )
    58  
    59  // SatoshiPerByte is number with units of satoshis per byte.
    60  type SatoshiPerByte float64
    61  
    62  // BtcPerKilobyte is number with units of bitcoins per kilobyte.
    63  type BtcPerKilobyte float64
    64  
    65  // ToBtcPerKb returns a float value that represents the given
    66  // SatoshiPerByte converted to satoshis per kb.
    67  func (rate SatoshiPerByte) ToBtcPerKb() BtcPerKilobyte {
    68  	// If our rate is the error value, return that.
    69  	if rate == SatoshiPerByte(-1.0) {
    70  		return -1.0
    71  	}
    72  
    73  	return BtcPerKilobyte(float64(rate) * bytePerKb * btcPerSatoshi)
    74  }
    75  
    76  // Fee returns the fee for a transaction of a given size for
    77  // the given fee rate.
    78  func (rate SatoshiPerByte) Fee(size uint32) btcutil.Amount {
    79  	// If our rate is the error value, return that.
    80  	if rate == SatoshiPerByte(-1) {
    81  		return btcutil.Amount(-1)
    82  	}
    83  
    84  	return btcutil.Amount(float64(rate) * float64(size))
    85  }
    86  
    87  // NewSatoshiPerByte creates a SatoshiPerByte from an Amount and a
    88  // size in bytes.
    89  func NewSatoshiPerByte(fee btcutil.Amount, size uint32) SatoshiPerByte {
    90  	return SatoshiPerByte(float64(fee) / float64(size))
    91  }
    92  
    93  // observedTransaction represents an observed transaction and some
    94  // additional data required for the fee estimation algorithm.
    95  type observedTransaction struct {
    96  	// A transaction hash.
    97  	hash chainhash.Hash
    98  
    99  	// The fee per byte of the transaction in satoshis.
   100  	feeRate SatoshiPerByte
   101  
   102  	// The block height when it was observed.
   103  	observed int32
   104  
   105  	// The height of the block in which it was mined.
   106  	// If the transaction has not yet been mined, it is zero.
   107  	mined int32
   108  }
   109  
   110  func (o *observedTransaction) Serialize(w io.Writer) {
   111  	binary.Write(w, binary.BigEndian, o.hash)
   112  	binary.Write(w, binary.BigEndian, o.feeRate)
   113  	binary.Write(w, binary.BigEndian, o.observed)
   114  	binary.Write(w, binary.BigEndian, o.mined)
   115  }
   116  
   117  func deserializeObservedTransaction(r io.Reader) (*observedTransaction, error) {
   118  	ot := observedTransaction{}
   119  
   120  	// The first 32 bytes should be a hash.
   121  	binary.Read(r, binary.BigEndian, &ot.hash)
   122  
   123  	// The next 8 are SatoshiPerByte
   124  	binary.Read(r, binary.BigEndian, &ot.feeRate)
   125  
   126  	// And next there are two uint32's.
   127  	binary.Read(r, binary.BigEndian, &ot.observed)
   128  	binary.Read(r, binary.BigEndian, &ot.mined)
   129  
   130  	return &ot, nil
   131  }
   132  
   133  // registeredBlock has the hash of a block and the list of transactions
   134  // it mined which had been previously observed by the FeeEstimator. It
   135  // is used if Rollback is called to reverse the effect of registering
   136  // a block.
   137  type registeredBlock struct {
   138  	hash         chainhash.Hash
   139  	transactions []*observedTransaction
   140  }
   141  
   142  func (rb *registeredBlock) serialize(w io.Writer, txs map[*observedTransaction]uint32) {
   143  	binary.Write(w, binary.BigEndian, rb.hash)
   144  
   145  	binary.Write(w, binary.BigEndian, uint32(len(rb.transactions)))
   146  	for _, o := range rb.transactions {
   147  		binary.Write(w, binary.BigEndian, txs[o])
   148  	}
   149  }
   150  
   151  // FeeEstimator manages the data necessary to create
   152  // fee estimations. It is safe for concurrent access.
   153  type FeeEstimator struct {
   154  	maxRollback uint32
   155  	binSize     int32
   156  
   157  	// The maximum number of replacements that can be made in a single
   158  	// bin per block. Default is estimateFeeMaxReplacements
   159  	maxReplacements int32
   160  
   161  	// The minimum number of blocks that can be registered with the fee
   162  	// estimator before it will provide answers.
   163  	minRegisteredBlocks uint32
   164  
   165  	// The last known height.
   166  	lastKnownHeight int32
   167  
   168  	// The number of blocks that have been registered.
   169  	numBlocksRegistered uint32
   170  
   171  	mtx      sync.RWMutex
   172  	observed map[chainhash.Hash]*observedTransaction
   173  	bin      [estimateFeeDepth][]*observedTransaction
   174  
   175  	// The cached estimates.
   176  	cached []SatoshiPerByte
   177  
   178  	// Transactions that have been removed from the bins. This allows us to
   179  	// revert in case of an orphaned block.
   180  	dropped []*registeredBlock
   181  }
   182  
   183  // NewFeeEstimator creates a FeeEstimator for which at most maxRollback blocks
   184  // can be unregistered and which returns an error unless minRegisteredBlocks
   185  // have been registered with it.
   186  func NewFeeEstimator(maxRollback, minRegisteredBlocks uint32) *FeeEstimator {
   187  	return &FeeEstimator{
   188  		maxRollback:         maxRollback,
   189  		minRegisteredBlocks: minRegisteredBlocks,
   190  		lastKnownHeight:     mining.UnminedHeight,
   191  		binSize:             estimateFeeBinSize,
   192  		maxReplacements:     estimateFeeMaxReplacements,
   193  		observed:            make(map[chainhash.Hash]*observedTransaction),
   194  		dropped:             make([]*registeredBlock, 0, maxRollback),
   195  	}
   196  }
   197  
   198  // ObserveTransaction is called when a new transaction is observed in the mempool.
   199  func (ef *FeeEstimator) ObserveTransaction(t *TxDesc) {
   200  	ef.mtx.Lock()
   201  	defer ef.mtx.Unlock()
   202  
   203  	// If we haven't seen a block yet we don't know when this one arrived,
   204  	// so we ignore it.
   205  	if ef.lastKnownHeight == mining.UnminedHeight {
   206  		return
   207  	}
   208  
   209  	hash := *t.Tx.Hash()
   210  	if _, ok := ef.observed[hash]; !ok {
   211  		size := uint32(GetTxVirtualSize(t.Tx))
   212  
   213  		ef.observed[hash] = &observedTransaction{
   214  			hash:     hash,
   215  			feeRate:  NewSatoshiPerByte(btcutil.Amount(t.Fee), size),
   216  			observed: t.Height,
   217  			mined:    mining.UnminedHeight,
   218  		}
   219  	}
   220  }
   221  
   222  // RegisterBlock informs the fee estimator of a new block to take into account.
   223  func (ef *FeeEstimator) RegisterBlock(block *btcutil.Block) error {
   224  	ef.mtx.Lock()
   225  	defer ef.mtx.Unlock()
   226  
   227  	// The previous sorted list is invalid, so delete it.
   228  	ef.cached = nil
   229  
   230  	height := block.Height()
   231  	if height != ef.lastKnownHeight+1 && ef.lastKnownHeight != mining.UnminedHeight {
   232  		return fmt.Errorf("intermediate block not recorded; current height is %d; new height is %d",
   233  			ef.lastKnownHeight, height)
   234  	}
   235  
   236  	// Update the last known height.
   237  	ef.lastKnownHeight = height
   238  	ef.numBlocksRegistered++
   239  
   240  	// Randomly order txs in block.
   241  	transactions := make(map[*btcutil.Tx]struct{})
   242  	for _, t := range block.Transactions() {
   243  		transactions[t] = struct{}{}
   244  	}
   245  
   246  	// Count the number of replacements we make per bin so that we don't
   247  	// replace too many.
   248  	var replacementCounts [estimateFeeDepth]int
   249  
   250  	// Keep track of which txs were dropped in case of an orphan block.
   251  	dropped := &registeredBlock{
   252  		hash:         *block.Hash(),
   253  		transactions: make([]*observedTransaction, 0, 100),
   254  	}
   255  
   256  	// Go through the txs in the block.
   257  	for t := range transactions {
   258  		hash := *t.Hash()
   259  
   260  		// Have we observed this tx in the mempool?
   261  		o, ok := ef.observed[hash]
   262  		if !ok {
   263  			continue
   264  		}
   265  
   266  		// Put the observed tx in the oppropriate bin.
   267  		blocksToConfirm := height - o.observed - 1
   268  
   269  		// This shouldn't happen if the fee estimator works correctly,
   270  		// but return an error if it does.
   271  		if o.mined != mining.UnminedHeight {
   272  			log.Error("Estimate fee: transaction ", hash.String(), " has already been mined")
   273  			return errors.New("Transaction has already been mined")
   274  		}
   275  
   276  		// This shouldn't happen but check just in case to avoid
   277  		// an out-of-bounds array index later.
   278  		if blocksToConfirm >= estimateFeeDepth {
   279  			continue
   280  		}
   281  
   282  		// Make sure we do not replace too many transactions per min.
   283  		if replacementCounts[blocksToConfirm] == int(ef.maxReplacements) {
   284  			continue
   285  		}
   286  
   287  		o.mined = height
   288  
   289  		replacementCounts[blocksToConfirm]++
   290  
   291  		bin := ef.bin[blocksToConfirm]
   292  
   293  		// Remove a random element and replace it with this new tx.
   294  		if len(bin) == int(ef.binSize) {
   295  			// Don't drop transactions we have just added from this same block.
   296  			l := int(ef.binSize) - replacementCounts[blocksToConfirm]
   297  			drop := rand.Intn(l)
   298  			dropped.transactions = append(dropped.transactions, bin[drop])
   299  
   300  			bin[drop] = bin[l-1]
   301  			bin[l-1] = o
   302  		} else {
   303  			bin = append(bin, o)
   304  		}
   305  		ef.bin[blocksToConfirm] = bin
   306  	}
   307  
   308  	// Go through the mempool for txs that have been in too long.
   309  	for hash, o := range ef.observed {
   310  		if o.mined == mining.UnminedHeight && height-o.observed >= estimateFeeDepth {
   311  			delete(ef.observed, hash)
   312  		}
   313  	}
   314  
   315  	// Add dropped list to history.
   316  	if ef.maxRollback == 0 {
   317  		return nil
   318  	}
   319  
   320  	if uint32(len(ef.dropped)) == ef.maxRollback {
   321  		ef.dropped = append(ef.dropped[1:], dropped)
   322  	} else {
   323  		ef.dropped = append(ef.dropped, dropped)
   324  	}
   325  
   326  	return nil
   327  }
   328  
   329  // LastKnownHeight returns the height of the last block which was registered.
   330  func (ef *FeeEstimator) LastKnownHeight() int32 {
   331  	ef.mtx.Lock()
   332  	defer ef.mtx.Unlock()
   333  
   334  	return ef.lastKnownHeight
   335  }
   336  
   337  // Rollback unregisters a recently registered block from the FeeEstimator.
   338  // This can be used to reverse the effect of an orphaned block on the fee
   339  // estimator. The maximum number of rollbacks allowed is given by
   340  // maxRollbacks.
   341  //
   342  // Note: not everything can be rolled back because some transactions are
   343  // deleted if they have been observed too long ago. That means the result
   344  // of Rollback won't always be exactly the same as if the last block had not
   345  // happened, but it should be close enough.
   346  func (ef *FeeEstimator) Rollback(hash *chainhash.Hash) error {
   347  	ef.mtx.Lock()
   348  	defer ef.mtx.Unlock()
   349  
   350  	// Find this block in the stack of recent registered blocks.
   351  	var n int
   352  	for n = 1; n <= len(ef.dropped); n++ {
   353  		if ef.dropped[len(ef.dropped)-n].hash.IsEqual(hash) {
   354  			break
   355  		}
   356  	}
   357  
   358  	if n > len(ef.dropped) {
   359  		return errors.New("no such block was recently registered")
   360  	}
   361  
   362  	for i := 0; i < n; i++ {
   363  		ef.rollback()
   364  	}
   365  
   366  	return nil
   367  }
   368  
   369  // rollback rolls back the effect of the last block in the stack
   370  // of registered blocks.
   371  func (ef *FeeEstimator) rollback() {
   372  	// The previous sorted list is invalid, so delete it.
   373  	ef.cached = nil
   374  
   375  	// pop the last list of dropped txs from the stack.
   376  	last := len(ef.dropped) - 1
   377  	if last == -1 {
   378  		// Cannot really happen because the exported calling function
   379  		// only rolls back a block already known to be in the list
   380  		// of dropped transactions.
   381  		return
   382  	}
   383  
   384  	dropped := ef.dropped[last]
   385  
   386  	// where we are in each bin as we replace txs?
   387  	var replacementCounters [estimateFeeDepth]int
   388  
   389  	// Go through the txs in the dropped block.
   390  	for _, o := range dropped.transactions {
   391  		// Which bin was this tx in?
   392  		blocksToConfirm := o.mined - o.observed - 1
   393  
   394  		bin := ef.bin[blocksToConfirm]
   395  
   396  		var counter = replacementCounters[blocksToConfirm]
   397  
   398  		// Continue to go through that bin where we left off.
   399  		for {
   400  			if counter >= len(bin) {
   401  				// Panic, as we have entered an unrecoverable invalid state.
   402  				panic(errors.New("illegal state: cannot rollback dropped transaction"))
   403  			}
   404  
   405  			prev := bin[counter]
   406  
   407  			if prev.mined == ef.lastKnownHeight {
   408  				prev.mined = mining.UnminedHeight
   409  
   410  				bin[counter] = o
   411  
   412  				counter++
   413  				break
   414  			}
   415  
   416  			counter++
   417  		}
   418  
   419  		replacementCounters[blocksToConfirm] = counter
   420  	}
   421  
   422  	// Continue going through bins to find other txs to remove
   423  	// which did not replace any other when they were entered.
   424  	for i, j := range replacementCounters {
   425  		for {
   426  			l := len(ef.bin[i])
   427  			if j >= l {
   428  				break
   429  			}
   430  
   431  			prev := ef.bin[i][j]
   432  
   433  			if prev.mined == ef.lastKnownHeight {
   434  				prev.mined = mining.UnminedHeight
   435  
   436  				newBin := append(ef.bin[i][0:j], ef.bin[i][j+1:l]...)
   437  				// TODO This line should prevent an unintentional memory
   438  				// leak but it causes a panic when it is uncommented.
   439  				// ef.bin[i][j] = nil
   440  				ef.bin[i] = newBin
   441  
   442  				continue
   443  			}
   444  
   445  			j++
   446  		}
   447  	}
   448  
   449  	ef.dropped = ef.dropped[0:last]
   450  
   451  	// The number of blocks the fee estimator has seen is decrimented.
   452  	ef.numBlocksRegistered--
   453  	ef.lastKnownHeight--
   454  }
   455  
   456  // estimateFeeSet is a set of txs that can that is sorted
   457  // by the fee per kb rate.
   458  type estimateFeeSet struct {
   459  	feeRate []SatoshiPerByte
   460  	bin     [estimateFeeDepth]uint32
   461  }
   462  
   463  func (b *estimateFeeSet) Len() int { return len(b.feeRate) }
   464  
   465  func (b *estimateFeeSet) Less(i, j int) bool {
   466  	return b.feeRate[i] > b.feeRate[j]
   467  }
   468  
   469  func (b *estimateFeeSet) Swap(i, j int) {
   470  	b.feeRate[i], b.feeRate[j] = b.feeRate[j], b.feeRate[i]
   471  }
   472  
   473  // estimateFee returns the estimated fee for a transaction
   474  // to confirm in confirmations blocks from now, given
   475  // the data set we have collected.
   476  func (b *estimateFeeSet) estimateFee(confirmations int) SatoshiPerByte {
   477  	if confirmations <= 0 {
   478  		return SatoshiPerByte(math.Inf(1))
   479  	}
   480  
   481  	if confirmations > estimateFeeDepth {
   482  		return 0
   483  	}
   484  
   485  	// We don't have any transactions!
   486  	if len(b.feeRate) == 0 {
   487  		return 0
   488  	}
   489  
   490  	var min, max int = 0, 0
   491  	for i := 0; i < confirmations-1; i++ {
   492  		min += int(b.bin[i])
   493  	}
   494  
   495  	max = min + int(b.bin[confirmations-1]) - 1
   496  	if max < min {
   497  		max = min
   498  	}
   499  	feeIndex := (min + max) / 2
   500  	if feeIndex >= len(b.feeRate) {
   501  		feeIndex = len(b.feeRate) - 1
   502  	}
   503  
   504  	return b.feeRate[feeIndex]
   505  }
   506  
   507  // newEstimateFeeSet creates a temporary data structure that
   508  // can be used to find all fee estimates.
   509  func (ef *FeeEstimator) newEstimateFeeSet() *estimateFeeSet {
   510  	set := &estimateFeeSet{}
   511  
   512  	capacity := 0
   513  	for i, b := range ef.bin {
   514  		l := len(b)
   515  		set.bin[i] = uint32(l)
   516  		capacity += l
   517  	}
   518  
   519  	set.feeRate = make([]SatoshiPerByte, capacity)
   520  
   521  	i := 0
   522  	for _, b := range ef.bin {
   523  		for _, o := range b {
   524  			set.feeRate[i] = o.feeRate
   525  			i++
   526  		}
   527  	}
   528  
   529  	sort.Sort(set)
   530  
   531  	return set
   532  }
   533  
   534  // estimates returns the set of all fee estimates from 1 to estimateFeeDepth
   535  // confirmations from now.
   536  func (ef *FeeEstimator) estimates() []SatoshiPerByte {
   537  	set := ef.newEstimateFeeSet()
   538  
   539  	estimates := make([]SatoshiPerByte, estimateFeeDepth)
   540  	for i := 0; i < estimateFeeDepth; i++ {
   541  		estimates[i] = set.estimateFee(i + 1)
   542  	}
   543  
   544  	return estimates
   545  }
   546  
   547  // EstimateFee estimates the fee per byte to have a tx confirmed a given
   548  // number of blocks from now.
   549  func (ef *FeeEstimator) EstimateFee(numBlocks uint32) (BtcPerKilobyte, error) {
   550  	ef.mtx.Lock()
   551  	defer ef.mtx.Unlock()
   552  
   553  	// If the number of registered blocks is below the minimum, return
   554  	// an error.
   555  	if ef.numBlocksRegistered < ef.minRegisteredBlocks {
   556  		return -1, errors.New("not enough blocks have been observed")
   557  	}
   558  
   559  	if numBlocks == 0 {
   560  		return -1, errors.New("cannot confirm transaction in zero blocks")
   561  	}
   562  
   563  	if numBlocks > estimateFeeDepth {
   564  		return -1, fmt.Errorf(
   565  			"can only estimate fees for up to %d blocks from now",
   566  			estimateFeeDepth)
   567  	}
   568  
   569  	// If there are no cached results, generate them.
   570  	if ef.cached == nil {
   571  		ef.cached = ef.estimates()
   572  	}
   573  
   574  	return ef.cached[int(numBlocks)-1].ToBtcPerKb(), nil
   575  }
   576  
   577  // In case the format for the serialized version of the FeeEstimator changes,
   578  // we use a version number. If the version number changes, it does not make
   579  // sense to try to upgrade a previous version to a new version. Instead, just
   580  // start fee estimation over.
   581  const estimateFeeSaveVersion = 1
   582  
   583  func deserializeRegisteredBlock(r io.Reader, txs map[uint32]*observedTransaction) (*registeredBlock, error) {
   584  	var lenTransactions uint32
   585  
   586  	rb := &registeredBlock{}
   587  	binary.Read(r, binary.BigEndian, &rb.hash)
   588  	binary.Read(r, binary.BigEndian, &lenTransactions)
   589  
   590  	rb.transactions = make([]*observedTransaction, lenTransactions)
   591  
   592  	for i := uint32(0); i < lenTransactions; i++ {
   593  		var index uint32
   594  		binary.Read(r, binary.BigEndian, &index)
   595  		rb.transactions[i] = txs[index]
   596  	}
   597  
   598  	return rb, nil
   599  }
   600  
   601  // FeeEstimatorState represents a saved FeeEstimator that can be
   602  // restored with data from an earlier session of the program.
   603  type FeeEstimatorState []byte
   604  
   605  // observedTxSet is a set of txs that can that is sorted
   606  // by hash. It exists for serialization purposes so that
   607  // a serialized state always comes out the same.
   608  type observedTxSet []*observedTransaction
   609  
   610  func (q observedTxSet) Len() int { return len(q) }
   611  
   612  func (q observedTxSet) Less(i, j int) bool {
   613  	return strings.Compare(q[i].hash.String(), q[j].hash.String()) < 0
   614  }
   615  
   616  func (q observedTxSet) Swap(i, j int) {
   617  	q[i], q[j] = q[j], q[i]
   618  }
   619  
   620  // Save records the current state of the FeeEstimator to a []byte that
   621  // can be restored later.
   622  func (ef *FeeEstimator) Save() FeeEstimatorState {
   623  	ef.mtx.Lock()
   624  	defer ef.mtx.Unlock()
   625  
   626  	// TODO figure out what the capacity should be.
   627  	w := bytes.NewBuffer(make([]byte, 0))
   628  
   629  	binary.Write(w, binary.BigEndian, uint32(estimateFeeSaveVersion))
   630  
   631  	// Insert basic parameters.
   632  	binary.Write(w, binary.BigEndian, &ef.maxRollback)
   633  	binary.Write(w, binary.BigEndian, &ef.binSize)
   634  	binary.Write(w, binary.BigEndian, &ef.maxReplacements)
   635  	binary.Write(w, binary.BigEndian, &ef.minRegisteredBlocks)
   636  	binary.Write(w, binary.BigEndian, &ef.lastKnownHeight)
   637  	binary.Write(w, binary.BigEndian, &ef.numBlocksRegistered)
   638  
   639  	// Put all the observed transactions in a sorted list.
   640  	var txCount uint32
   641  	ots := make([]*observedTransaction, len(ef.observed))
   642  	for hash := range ef.observed {
   643  		ots[txCount] = ef.observed[hash]
   644  		txCount++
   645  	}
   646  
   647  	sort.Sort(observedTxSet(ots))
   648  
   649  	txCount = 0
   650  	observed := make(map[*observedTransaction]uint32)
   651  	binary.Write(w, binary.BigEndian, uint32(len(ef.observed)))
   652  	for _, ot := range ots {
   653  		ot.Serialize(w)
   654  		observed[ot] = txCount
   655  		txCount++
   656  	}
   657  
   658  	// Save all the right bins.
   659  	for _, list := range ef.bin {
   660  
   661  		binary.Write(w, binary.BigEndian, uint32(len(list)))
   662  
   663  		for _, o := range list {
   664  			binary.Write(w, binary.BigEndian, observed[o])
   665  		}
   666  	}
   667  
   668  	// Dropped transactions.
   669  	binary.Write(w, binary.BigEndian, uint32(len(ef.dropped)))
   670  	for _, registered := range ef.dropped {
   671  		registered.serialize(w, observed)
   672  	}
   673  
   674  	// Commit the tx and return.
   675  	return FeeEstimatorState(w.Bytes())
   676  }
   677  
   678  // RestoreFeeEstimator takes a FeeEstimatorState that was previously
   679  // returned by Save and restores it to a FeeEstimator
   680  func RestoreFeeEstimator(data FeeEstimatorState) (*FeeEstimator, error) {
   681  	r := bytes.NewReader([]byte(data))
   682  
   683  	// Check version
   684  	var version uint32
   685  	err := binary.Read(r, binary.BigEndian, &version)
   686  	if err != nil {
   687  		return nil, err
   688  	}
   689  	if version != estimateFeeSaveVersion {
   690  		return nil, fmt.Errorf("Incorrect version: expected %d found %d", estimateFeeSaveVersion, version)
   691  	}
   692  
   693  	ef := &FeeEstimator{
   694  		observed: make(map[chainhash.Hash]*observedTransaction),
   695  	}
   696  
   697  	// Read basic parameters.
   698  	binary.Read(r, binary.BigEndian, &ef.maxRollback)
   699  	binary.Read(r, binary.BigEndian, &ef.binSize)
   700  	binary.Read(r, binary.BigEndian, &ef.maxReplacements)
   701  	binary.Read(r, binary.BigEndian, &ef.minRegisteredBlocks)
   702  	binary.Read(r, binary.BigEndian, &ef.lastKnownHeight)
   703  	binary.Read(r, binary.BigEndian, &ef.numBlocksRegistered)
   704  
   705  	// Read transactions.
   706  	var numObserved uint32
   707  	observed := make(map[uint32]*observedTransaction)
   708  	binary.Read(r, binary.BigEndian, &numObserved)
   709  	for i := uint32(0); i < numObserved; i++ {
   710  		ot, err := deserializeObservedTransaction(r)
   711  		if err != nil {
   712  			return nil, err
   713  		}
   714  		observed[i] = ot
   715  		ef.observed[ot.hash] = ot
   716  	}
   717  
   718  	// Read bins.
   719  	for i := 0; i < estimateFeeDepth; i++ {
   720  		var numTransactions uint32
   721  		binary.Read(r, binary.BigEndian, &numTransactions)
   722  		bin := make([]*observedTransaction, numTransactions)
   723  		for j := uint32(0); j < numTransactions; j++ {
   724  			var index uint32
   725  			binary.Read(r, binary.BigEndian, &index)
   726  
   727  			var exists bool
   728  			bin[j], exists = observed[index]
   729  			if !exists {
   730  				return nil, fmt.Errorf("Invalid transaction reference %d", index)
   731  			}
   732  		}
   733  		ef.bin[i] = bin
   734  	}
   735  
   736  	// Read dropped transactions.
   737  	var numDropped uint32
   738  	binary.Read(r, binary.BigEndian, &numDropped)
   739  	ef.dropped = make([]*registeredBlock, numDropped)
   740  	for i := uint32(0); i < numDropped; i++ {
   741  		var err error
   742  		ef.dropped[int(i)], err = deserializeRegisteredBlock(r, observed)
   743  		if err != nil {
   744  			return nil, err
   745  		}
   746  	}
   747  
   748  	return ef, nil
   749  }