Skip to content

Commit

Permalink
Add checks to regenerate fee estimator if an invalid state is
Browse files Browse the repository at this point in the history
detected. Change default minimum registered blocks from 5 to 3.

Run goclean.sh
  • Loading branch information
Daniel Krawisz committed Mar 28, 2017
1 parent d308bc3 commit cd03910
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 40 deletions.
11 changes: 10 additions & 1 deletion blockmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -1160,7 +1160,16 @@ func (b *blockManager) handleNotifyMsg(notification *blockchain.Notification) {

// Register block with the fee estimator if enabled.
if b.server.feeEstimator != nil {
b.server.feeEstimator.RegisterBlock(block)
err := b.server.feeEstimator.RegisterBlock(block)

// If an error is somehow generated then the fee estimator
// has entered an invalid state. Since it doesn't know how
// to recover, create a new one.
if err != nil {
b.server.feeEstimator = mempool.NewFeeEstimator(
mempool.DefaultEstimateFeeMaxRollback,
mempool.DefaultEstimateFeeMinRegisteredBlocks)
}
}

if r := b.server.rpcServer; r != nil {
Expand Down
20 changes: 14 additions & 6 deletions mempool/estimatefee.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const (
// DefaultEstimateFeeMinRegisteredBlocks is the default minimum
// number of blocks which must be observed by the fee estimator before
// it will provide fee estimations.
DefaultEstimateFeeMinRegisteredBlocks = 5
DefaultEstimateFeeMinRegisteredBlocks = 3

bytePerKb = 1024

Expand Down Expand Up @@ -327,6 +327,14 @@ func (ef *FeeEstimator) RegisterBlock(block *btcutil.Block) error {
return nil
}

// LastKnownHeight returns the height of the last block which was registered.
func (ef *FeeEstimator) LastKnownHeight() int32 {
ef.mtx.Lock()
defer ef.mtx.Unlock()

return ef.lastKnownHeight
}

// Rollback unregisters a recently registered block from the FeeEstimator.
// This can be used to reverse the effect of an orphaned block on the fee
// estimator. The maximum number of rollbacks allowed is given by
Expand All @@ -349,7 +357,7 @@ func (ef *FeeEstimator) Rollback(hash *chainhash.Hash) error {
}

if n > len(ef.dropped) {
return errors.New("No such block was recently registered.")
return errors.New("No such block was recently registered")
}

for i := 0; i < n; i++ {
Expand Down Expand Up @@ -392,7 +400,7 @@ func (ef *FeeEstimator) rollback() {
for {
if counter >= len(bin) {
// Panic, as we have entered an unrecoverable invalid state.
panic(errors.New("Illegal state: cannot rollback dropped transaction!"))
panic(errors.New("Illegal state: cannot rollback dropped transaction"))
}

prev := bin[counter]
Expand Down Expand Up @@ -546,16 +554,16 @@ func (ef *FeeEstimator) EstimateFee(numBlocks uint32) (BtcPerKilobyte, error) {
// If the number of registered blocks is below the minimum, return
// an error.
if ef.numBlocksRegistered < ef.minRegisteredBlocks {
return -1, errors.New("Not enough blocks have been observed.")
return -1, errors.New("Not enough blocks have been observed")
}

if numBlocks == 0 {
return -1, errors.New("Cannot confirm transaction in zero blocks.")
return -1, errors.New("Cannot confirm transaction in zero blocks")
}

if numBlocks > estimateFeeDepth {
return -1, fmt.Errorf(
"Can only estimate fees for up to %d blocks from now.",
"Can only estimate fees for up to %d blocks from now",
estimateFeeBinSize)
}

Expand Down
2 changes: 1 addition & 1 deletion mempool/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ func (mp *TxPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil
if mp.cfg.AddrIndex != nil {
mp.cfg.AddrIndex.AddUnconfirmedTx(tx, utxoView)
}

// Record this tx for fee estimation if enabled.
if mp.cfg.FeeEstimator != nil {
mp.cfg.FeeEstimator.ObserveTransaction(txD)
Expand Down
2 changes: 1 addition & 1 deletion rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,7 @@ func handleEstimateFee(s *rpcServer, cmd interface{}, closeChan <-chan struct{})
c := cmd.(*btcjson.EstimateFeeCmd)

if c.NumBlocks <= 0 {
return -1.0, errors.New("Parameter NumBlocks must be positive.")
return -1.0, errors.New("Parameter NumBlocks must be positive")
}

feeRate, err := s.server.feeEstimator.EstimateFee(uint32(c.NumBlocks))
Expand Down
64 changes: 33 additions & 31 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2367,34 +2367,6 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param
sigCache: txscript.NewSigCache(cfg.SigCacheMaxSize),
}

// Search for a FeeEstimator state in the database. If none can be found
// or if it cannot be loaded, create a new one.
db.Update(func(tx database.Tx) error {
metadata := tx.Metadata()
feeEstimationData := metadata.Get(mempool.EstimateFeeDatabaseKey)
if feeEstimationData != nil {
// delete it from the database so that we don't try to restore the
// same thing again somehow.
metadata.Delete(mempool.EstimateFeeDatabaseKey)

// If there is an error, log it and make a new fee estimator.
var err error
s.feeEstimator, err = mempool.RestoreFeeEstimator(feeEstimationData)

if err != nil {
peerLog.Errorf("Failed restore fee estimator %v", err)
}
}

return nil
})

if s.feeEstimator == nil {
s.feeEstimator = mempool.NewFeeEstimator(
mempool.DefaultEstimateFeeMaxRollback,
mempool.DefaultEstimateFeeMinRegisteredBlocks)
}

// Create the transaction and address indexes if needed.
//
// CAUTION: the txindex needs to be first in the indexes array because
Expand Down Expand Up @@ -2433,6 +2405,36 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param
}
s.blockManager = bm

// Search for a FeeEstimator state in the database. If none can be found
// or if it cannot be loaded, create a new one.
db.Update(func(tx database.Tx) error {
metadata := tx.Metadata()
feeEstimationData := metadata.Get(mempool.EstimateFeeDatabaseKey)
if feeEstimationData != nil {
// delete it from the database so that we don't try to restore the
// same thing again somehow.
metadata.Delete(mempool.EstimateFeeDatabaseKey)

// If there is an error, log it and make a new fee estimator.
var err error
s.feeEstimator, err = mempool.RestoreFeeEstimator(feeEstimationData)

if err != nil {
peerLog.Errorf("Failed restore fee estimator %v", err)
}
}

return nil
})

// If no feeEstimator has been found, or if the one that has been found
// is behind somehow, create a new one and start over.
if s.feeEstimator == nil || s.feeEstimator.LastKnownHeight() != s.blockManager.chain.BestSnapshot().Height {
s.feeEstimator = mempool.NewFeeEstimator(
mempool.DefaultEstimateFeeMaxRollback,
mempool.DefaultEstimateFeeMinRegisteredBlocks)
}

txC := mempool.Config{
Policy: mempool.Policy{
DisableRelayPriority: cfg.NoRelayPriority,
Expand All @@ -2451,9 +2453,9 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param
CalcSequenceLock: func(tx *btcutil.Tx, view *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error) {
return bm.chain.CalcSequenceLock(tx, view, true)
},
SigCache: s.sigCache,
AddrIndex: s.addrIndex,
FeeEstimator: s.feeEstimator,
SigCache: s.sigCache,
AddrIndex: s.addrIndex,
FeeEstimator: s.feeEstimator,
}
s.txMemPool = mempool.New(&txC)

Expand Down

0 comments on commit cd03910

Please sign in to comment.