From 8e1a062c11ab12e85fadc4bbaef27abf2621555d Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 24 Dec 2020 22:14:25 -0600 Subject: [PATCH] blockchain: Explicit hash in estimate stake diff. This modifies EstimateNextStakeDifficulty to accept a hash instead of using the current chain tip as part of an overall effort to make the blockchain module more explicit and less reliant on the current state. It also updates a few comments to match reality while here. --- blockchain/difficulty.go | 22 +++++++------------- internal/rpcserver/interface.go | 2 +- internal/rpcserver/rpcserver.go | 15 ++++++------- internal/rpcserver/rpcserverhandlers_test.go | 12 +++++------ 4 files changed, 23 insertions(+), 28 deletions(-) diff --git a/blockchain/difficulty.go b/blockchain/difficulty.go index f05439443c..16a824881c 100644 --- a/blockchain/difficulty.go +++ b/blockchain/difficulty.go @@ -49,9 +49,6 @@ func (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) uint32 { // calcNextRequiredDifficulty calculates the required difficulty for the block // after the passed previous block node based on the difficulty retarget rules. -// This function differs from the exported CalcNextRequiredDifficulty in that -// the exported version uses the current best chain as the previous block node -// while this function accepts any block node. func (b *BlockChain) calcNextRequiredDifficulty(curNode *blockNode, newBlockTime time.Time) uint32 { // Get the old difficulty; if we aren't at a block height where it changes, // just return this. @@ -678,10 +675,6 @@ func (b *BlockChain) calcNextRequiredStakeDifficultyV2(curNode *blockNode) int64 // the block after the passed previous block node based on the active stake // difficulty retarget rules. // -// This function differs from the exported CalcNextRequiredDifficulty in that -// the exported version uses the current best chain as the previous block node -// while this function accepts any block node. -// // This function MUST be called with the chain state lock held (for writes). func (b *BlockChain) calcNextRequiredStakeDifficulty(curNode *blockNode) (int64, error) { // Determine the correct deployment version for the new stake difficulty @@ -1172,10 +1165,6 @@ func (b *BlockChain) estimateNextStakeDifficultyV2(curNode *blockNode, newTicket // // The stake difficulty algorithm is selected based on the active rules. // -// This function differs from the exported EstimateNextStakeDifficulty in that -// the exported version uses the current best chain as the block node while this -// function accepts any block node. -// // This function MUST be called with the chain state lock held (for writes). func (b *BlockChain) estimateNextStakeDifficulty(curNode *blockNode, newTickets int64, useMaxTickets bool) (int64, error) { // Determine the correct deployment version for the new stake difficulty @@ -1214,10 +1203,15 @@ func (b *BlockChain) estimateNextStakeDifficulty(curNode *blockNode, newTickets // the interval. // // This function is safe for concurrent access. -func (b *BlockChain) EstimateNextStakeDifficulty(newTickets int64, useMaxTickets bool) (int64, error) { +func (b *BlockChain) EstimateNextStakeDifficulty(hash *chainhash.Hash, newTickets int64, useMaxTickets bool) (int64, error) { + node := b.index.LookupNode(hash) + if node == nil || !b.index.CanValidate(node) { + return 0, unknownBlockError(hash) + } + b.chainLock.Lock() - estimate, err := b.estimateNextStakeDifficulty(b.bestChain.Tip(), - newTickets, useMaxTickets) + estimate, err := b.estimateNextStakeDifficulty(node, newTickets, + useMaxTickets) b.chainLock.Unlock() return estimate, err } diff --git a/internal/rpcserver/interface.go b/internal/rpcserver/interface.go index 3fb0daf7a7..33b6c9f2f3 100644 --- a/internal/rpcserver/interface.go +++ b/internal/rpcserver/interface.go @@ -296,7 +296,7 @@ type Chain interface { // interval unless the flag to use max tickets is set in which case it will use // the max possible number of tickets that can be purchased in the remainder of // the interval. - EstimateNextStakeDifficulty(newTickets int64, useMaxTickets bool) (int64, error) + EstimateNextStakeDifficulty(hash *chainhash.Hash, newTickets int64, useMaxTickets bool) (int64, error) // FetchUtxoEntry loads and returns the unspent transaction output entry for the // passed hash from the point of view of the end of the main chain. diff --git a/internal/rpcserver/rpcserver.go b/internal/rpcserver/rpcserver.go index 36dcbc5594..8fd6885b45 100644 --- a/internal/rpcserver/rpcserver.go +++ b/internal/rpcserver/rpcserver.go @@ -1429,14 +1429,15 @@ func handleEstimateStakeDiff(_ context.Context, s *Server, cmd interface{}) (int // Minimum possible stake difficulty. chain := s.cfg.Chain - min, err := chain.EstimateNextStakeDifficulty(0, false) + best := chain.BestSnapshot() + min, err := chain.EstimateNextStakeDifficulty(&best.Hash, 0, false) if err != nil { return nil, rpcInternalError(err.Error(), "Could not "+ "estimate next minimum stake difficulty") } // Maximum possible stake difficulty. - max, err := chain.EstimateNextStakeDifficulty(0, true) + max, err := chain.EstimateNextStakeDifficulty(&best.Hash, 0, true) if err != nil { return nil, rpcInternalError(err.Error(), "Could not "+ "estimate next maximum stake difficulty") @@ -1446,7 +1447,7 @@ func handleEstimateStakeDiff(_ context.Context, s *Server, cmd interface{}) (int // since the last retarget to get the number of tickets per block, // then use that to estimate the next stake difficulty. params := s.cfg.ChainParams - bestHeight := chain.BestSnapshot().Height + bestHeight := best.Height lastAdjustment := (bestHeight / params.StakeDiffWindowSize) * params.StakeDiffWindowSize nextAdjustment := ((bestHeight / params.StakeDiffWindowSize) + 1) * @@ -1464,8 +1465,8 @@ func handleEstimateStakeDiff(_ context.Context, s *Server, cmd interface{}) (int remaining := float64(nextAdjustment - bestHeight - 1) averagePerBlock := float64(totalTickets) / blocksSince expectedTickets := int64(math.Floor(averagePerBlock * remaining)) - expected, err := chain.EstimateNextStakeDifficulty(expectedTickets, - false) + expected, err := chain.EstimateNextStakeDifficulty(&best.Hash, + expectedTickets, false) if err != nil { return nil, rpcInternalError(err.Error(), "Could not "+ "estimate next stake difficulty") @@ -1474,8 +1475,8 @@ func handleEstimateStakeDiff(_ context.Context, s *Server, cmd interface{}) (int // User-specified stake difficulty, if they asked for one. var userEstFltPtr *float64 if c.Tickets != nil { - userEst, err := chain.EstimateNextStakeDifficulty(int64(*c.Tickets), - false) + userEst, err := chain.EstimateNextStakeDifficulty(&best.Hash, + int64(*c.Tickets), false) if err != nil { return nil, rpcInternalError(err.Error(), "Could not "+ "estimate next user specified stake difficulty") diff --git a/internal/rpcserver/rpcserverhandlers_test.go b/internal/rpcserver/rpcserverhandlers_test.go index e7a796e233..d7c2e7acd8 100644 --- a/internal/rpcserver/rpcserverhandlers_test.go +++ b/internal/rpcserver/rpcserverhandlers_test.go @@ -148,7 +148,7 @@ type testRPCChain struct { convertUtxosToMinimalOutputs []*stake.MinimalOutput countVoteVersion uint32 countVoteVersionErr error - estimateNextStakeDifficultyFn func(newTickets int64, useMaxTickets bool) (diff int64, err error) + estimateNextStakeDifficultyFn func(hash *chainhash.Hash, newTickets int64, useMaxTickets bool) (diff int64, err error) fetchUtxoEntry UtxoEntry fetchUtxoEntryErr error fetchUtxoStats *blockchain.UtxoStats @@ -273,8 +273,8 @@ func (c *testRPCChain) CountVoteVersion(version uint32) (uint32, error) { } // EstimateNextStakeDifficulty returns a mocked estimated next stake difficulty. -func (c *testRPCChain) EstimateNextStakeDifficulty(newTickets int64, useMaxTickets bool) (int64, error) { - return c.estimateNextStakeDifficultyFn(newTickets, useMaxTickets) +func (c *testRPCChain) EstimateNextStakeDifficulty(hash *chainhash.Hash, newTickets int64, useMaxTickets bool) (int64, error) { + return c.estimateNextStakeDifficultyFn(hash, newTickets, useMaxTickets) } // FetchUtxoEntry returns a mocked UtxoEntry. @@ -1407,7 +1407,7 @@ func defaultMockRPCChain() *testRPCChain { Value: 0, Version: 0, }}, - estimateNextStakeDifficultyFn: func(int64, bool) (int64, error) { + estimateNextStakeDifficultyFn: func(*chainhash.Hash, int64, bool) (int64, error) { return 14336790201, nil }, fetchUtxoEntry: &testRPCUtxoEntry{ @@ -2750,8 +2750,8 @@ func TestHandleEstimateStakeDiff(t *testing.T) { &userQItem, } } - estimateFn := func(queue []*stakeDiffQueueItem) func(int64, bool) (int64, error) { - return func(int64, bool) (int64, error) { + estimateFn := func(queue []*stakeDiffQueueItem) func(*chainhash.Hash, int64, bool) (int64, error) { + return func(*chainhash.Hash, int64, bool) (int64, error) { defer func() { queue = queue[1:] }() return queue[0].diff, queue[0].err }