github.com/decred/dcrd/blockchain@v1.2.1/stakeversion.go (about)

     1  // Copyright (c) 2016-2018 The Decred 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 blockchain
     6  
     7  import (
     8  	"encoding/binary"
     9  	"errors"
    10  	"fmt"
    11  
    12  	"github.com/decred/dcrd/chaincfg/chainhash"
    13  )
    14  
    15  var (
    16  	errVoterVersionMajorityNotFound = errors.New("voter version majority " +
    17  		"not found")
    18  	errStakeVersionMajorityNotFound = errors.New("stake version majority " +
    19  		"not found")
    20  )
    21  
    22  // stakeMajorityCacheVersionKey creates a map key that is comprised of a stake
    23  // version and a hash.  This is used for caches that require a version in
    24  // addition to a simple hash.
    25  func stakeMajorityCacheVersionKey(version uint32, hash *chainhash.Hash) [stakeMajorityCacheKeySize]byte {
    26  	var key [stakeMajorityCacheKeySize]byte
    27  	binary.LittleEndian.PutUint32(key[0:], version)
    28  	copy(key[4:], hash[:])
    29  	return key
    30  }
    31  
    32  // calcWantHeight calculates the height of the final block of the previous
    33  // interval given a stake validation height, stake validation interval, and
    34  // block height.
    35  func calcWantHeight(stakeValidationHeight, interval, height int64) int64 {
    36  	// The adjusted height accounts for the fact the starting validation
    37  	// height does not necessarily start on an interval and thus the
    38  	// intervals might not be zero-based.
    39  	intervalOffset := stakeValidationHeight % interval
    40  	adjustedHeight := height - intervalOffset - 1
    41  	return (adjustedHeight - ((adjustedHeight + 1) % interval)) +
    42  		intervalOffset
    43  }
    44  
    45  // CalcWantHeight calculates the height of the final block of the previous
    46  // interval given a block height.
    47  func (b *BlockChain) CalcWantHeight(interval, height int64) int64 {
    48  	return calcWantHeight(b.chainParams.StakeValidationHeight, interval,
    49  		height)
    50  }
    51  
    52  // findStakeVersionPriorNode walks the chain backwards from prevNode until it
    53  // reaches the final block of the previous stake version interval and returns
    54  // that node.  The returned node will be nil when the provided prevNode is too
    55  // low such that there is no previous stake version interval due to falling
    56  // prior to the stake validation interval.
    57  //
    58  // This function MUST be called with the chain state lock held (for writes).
    59  func (b *BlockChain) findStakeVersionPriorNode(prevNode *blockNode) *blockNode {
    60  	// Check to see if the blockchain is high enough to begin accounting
    61  	// stake versions.
    62  	svh := b.chainParams.StakeValidationHeight
    63  	svi := b.chainParams.StakeVersionInterval
    64  	nextHeight := prevNode.height + 1
    65  	if nextHeight < svh+svi {
    66  		return nil
    67  	}
    68  
    69  	wantHeight := calcWantHeight(svh, svi, nextHeight)
    70  	return prevNode.Ancestor(wantHeight)
    71  }
    72  
    73  // isStakeMajorityVersion determines if minVer requirement is met based on
    74  // prevNode.  The function always uses the stake versions of the prior window.
    75  // For example, if StakeVersionInterval = 11 and StakeValidationHeight = 13 the
    76  // windows start at 13 + (11 * 2) 25 and are as follows: 24-34, 35-45, 46-56 ...
    77  // If height comes in at 35 we use the 24-34 window, up to height 45.
    78  // If height comes in at 46 we use the 35-45 window, up to height 56 etc.
    79  //
    80  // This function MUST be called with the chain state lock held (for writes).
    81  func (b *BlockChain) isStakeMajorityVersion(minVer uint32, prevNode *blockNode) bool {
    82  	// Walk blockchain backwards to calculate version.
    83  	node := b.findStakeVersionPriorNode(prevNode)
    84  	if node == nil {
    85  		return 0 >= minVer
    86  	}
    87  
    88  	// Generate map key and look up cached result.
    89  	key := stakeMajorityCacheVersionKey(minVer, &node.hash)
    90  	if result, ok := b.isStakeMajorityVersionCache[key]; ok {
    91  		return result
    92  	}
    93  
    94  	// Tally how many of the block headers in the previous stake version
    95  	// validation interval have their stake version set to at least the
    96  	// requested minimum version.
    97  	versionCount := int32(0)
    98  	iterNode := node
    99  	for i := int64(0); i < b.chainParams.StakeVersionInterval && iterNode != nil; i++ {
   100  		if iterNode.stakeVersion >= minVer {
   101  			versionCount++
   102  		}
   103  
   104  		iterNode = iterNode.parent
   105  	}
   106  
   107  	// Determine the required amount of votes to reach supermajority.
   108  	numRequired := int32(b.chainParams.StakeVersionInterval) *
   109  		b.chainParams.StakeMajorityMultiplier /
   110  		b.chainParams.StakeMajorityDivisor
   111  
   112  	// Cache result.
   113  	result := versionCount >= numRequired
   114  	b.isStakeMajorityVersionCache[key] = result
   115  
   116  	return result
   117  }
   118  
   119  // calcPriorStakeVersion calculates the header stake version of the prior
   120  // interval.  The function walks the chain backwards by one interval and then
   121  // it performs a standard majority calculation.
   122  //
   123  // This function MUST be called with the chain state lock held (for writes).
   124  func (b *BlockChain) calcPriorStakeVersion(prevNode *blockNode) (uint32, error) {
   125  	// Walk blockchain backwards to calculate version.
   126  	node := b.findStakeVersionPriorNode(prevNode)
   127  	if node == nil {
   128  		return 0, nil
   129  	}
   130  
   131  	// Check cache.
   132  	if result, ok := b.calcPriorStakeVersionCache[node.hash]; ok {
   133  		return result, nil
   134  	}
   135  
   136  	// Tally how many of each stake version the block headers in the previous stake
   137  	// version validation interval have.
   138  	versions := make(map[uint32]int32) // [version][count]
   139  	iterNode := node
   140  	for i := int64(0); i < b.chainParams.StakeVersionInterval && iterNode != nil; i++ {
   141  		versions[iterNode.stakeVersion]++
   142  
   143  		iterNode = iterNode.parent
   144  	}
   145  
   146  	// Determine the required amount of votes to reach supermajority.
   147  	numRequired := int32(b.chainParams.StakeVersionInterval) *
   148  		b.chainParams.StakeMajorityMultiplier /
   149  		b.chainParams.StakeMajorityDivisor
   150  
   151  	for version, count := range versions {
   152  		if count >= numRequired {
   153  			b.calcPriorStakeVersionCache[node.hash] = version
   154  			return version, nil
   155  		}
   156  	}
   157  
   158  	return 0, errStakeVersionMajorityNotFound
   159  }
   160  
   161  // calcVoterVersionInterval tallies all voter versions in an interval and
   162  // returns a version that has reached 75% majority.  This function MUST be
   163  // called with a node that is the final node in a valid stake version interval
   164  // and greater than or equal to the stake validation height or it will result in
   165  // an assertion error.
   166  //
   167  // This function is really meant to be called internally only from this file.
   168  //
   169  // This function MUST be called with the chain state lock held (for writes).
   170  func (b *BlockChain) calcVoterVersionInterval(prevNode *blockNode) (uint32, error) {
   171  	// Ensure the provided node is the final node in a valid stake version
   172  	// interval and is greater than or equal to the stake validation height
   173  	// since the logic below relies on these assumptions.
   174  	svh := b.chainParams.StakeValidationHeight
   175  	svi := b.chainParams.StakeVersionInterval
   176  	expectedHeight := calcWantHeight(svh, svi, prevNode.height+1)
   177  	if prevNode.height != expectedHeight || expectedHeight < svh {
   178  		return 0, AssertError(fmt.Sprintf("calcVoterVersionInterval "+
   179  			"must be called with a node that is the final node "+
   180  			"in a stake version interval -- called with node %s "+
   181  			"(height %d)", prevNode.hash, prevNode.height))
   182  	}
   183  
   184  	// See if we have cached results.
   185  	if result, ok := b.calcVoterVersionIntervalCache[prevNode.hash]; ok {
   186  		return result, nil
   187  	}
   188  
   189  	// Tally both the total number of votes in the previous stake version validation
   190  	// interval and how many of each version those votes have.
   191  	versions := make(map[uint32]int32) // [version][count]
   192  	totalVotesFound := int32(0)
   193  	iterNode := prevNode
   194  	for i := int64(0); i < svi && iterNode != nil; i++ {
   195  		totalVotesFound += int32(len(iterNode.votes))
   196  		for _, v := range iterNode.votes {
   197  			versions[v.Version]++
   198  		}
   199  
   200  		iterNode = iterNode.parent
   201  	}
   202  
   203  	// Determine the required amount of votes to reach supermajority.
   204  	numRequired := totalVotesFound * b.chainParams.StakeMajorityMultiplier /
   205  		b.chainParams.StakeMajorityDivisor
   206  
   207  	for version, count := range versions {
   208  		if count >= numRequired {
   209  			b.calcVoterVersionIntervalCache[prevNode.hash] = version
   210  			return version, nil
   211  		}
   212  	}
   213  
   214  	return 0, errVoterVersionMajorityNotFound
   215  }
   216  
   217  // calcVoterVersion calculates the last prior valid majority stake version.  If
   218  // the current interval does not have a majority stake version it'll go back to
   219  // the prior interval.  It'll keep going back up to the minimum height at which
   220  // point we know the version was 0.
   221  //
   222  // This function MUST be called with the chain state lock held (for writes).
   223  func (b *BlockChain) calcVoterVersion(prevNode *blockNode) (uint32, *blockNode) {
   224  	// Walk blockchain backwards to find interval.
   225  	node := b.findStakeVersionPriorNode(prevNode)
   226  
   227  	// Iterate over versions until a majority is found.  Don't try to count
   228  	// votes before the stake validation height since there could not
   229  	// possibly have been any.
   230  	for node != nil && node.height >= b.chainParams.StakeValidationHeight {
   231  		version, err := b.calcVoterVersionInterval(node)
   232  		if err == nil {
   233  			return version, node
   234  		}
   235  		if err != errVoterVersionMajorityNotFound {
   236  			break
   237  		}
   238  
   239  		node = node.RelativeAncestor(b.chainParams.StakeVersionInterval)
   240  	}
   241  
   242  	// No majority version found.
   243  	return 0, nil
   244  }
   245  
   246  // calcStakeVersion calculates the header stake version based on voter versions.
   247  // If there is a majority of voter versions it uses the header stake version to
   248  // prevent reverting to a prior version.
   249  //
   250  // This function MUST be called with the chain state lock held (for writes).
   251  func (b *BlockChain) calcStakeVersion(prevNode *blockNode) uint32 {
   252  	version, node := b.calcVoterVersion(prevNode)
   253  	if version == 0 || node == nil {
   254  		// Short circuit.
   255  		return 0
   256  	}
   257  
   258  	// Check cache.
   259  	if result, ok := b.calcStakeVersionCache[node.hash]; ok {
   260  		return result
   261  	}
   262  
   263  	// Walk chain backwards to start of node interval (start of current
   264  	// period) Note that calcWantHeight returns the LAST height of the
   265  	// prior interval; hence the + 1.
   266  	startIntervalHeight := calcWantHeight(b.chainParams.StakeValidationHeight,
   267  		b.chainParams.StakeVersionInterval, node.height) + 1
   268  	startNode := node.Ancestor(startIntervalHeight)
   269  	if startNode == nil {
   270  		// Note that should this not be possible to hit because a
   271  		// majority voter version was obtained above, which means there
   272  		// is at least an interval of nodes.  However, be paranoid.
   273  		b.calcStakeVersionCache[node.hash] = 0
   274  	}
   275  
   276  	// See if we are enforcing V3 blocks yet.  Just return V0 since it
   277  	// wasn't enforced and therefore irrelevant.
   278  	if !b.isMajorityVersion(3, startNode,
   279  		b.chainParams.BlockRejectNumRequired) {
   280  		b.calcStakeVersionCache[node.hash] = 0
   281  		return 0
   282  	}
   283  
   284  	// Don't allow the stake version to go backwards once it has been locked
   285  	// in by a previous majority, even if the majority of votes are now a
   286  	// lower version.
   287  	if b.isStakeMajorityVersion(version, node) {
   288  		priorVersion, _ := b.calcPriorStakeVersion(node)
   289  		if priorVersion > version {
   290  			version = priorVersion
   291  		}
   292  	}
   293  
   294  	b.calcStakeVersionCache[node.hash] = version
   295  	return version
   296  }
   297  
   298  // calcStakeVersionByHash calculates the last prior valid majority stake
   299  // version.  If the current interval does not have a majority stake version
   300  // it'll go back to the prior interval.  It'll keep going back up to the
   301  // minimum height at which point we know the version was 0.
   302  //
   303  // This function MUST be called with the chain state lock held (for writes).
   304  func (b *BlockChain) calcStakeVersionByHash(hash *chainhash.Hash) (uint32, error) {
   305  	prevNode := b.index.LookupNode(hash)
   306  	if prevNode == nil {
   307  		return 0, fmt.Errorf("block %s is not known", hash)
   308  	}
   309  
   310  	return b.calcStakeVersion(prevNode), nil
   311  }
   312  
   313  // CalcStakeVersionByHash calculates the expected stake version for the block
   314  // AFTER provided block hash.
   315  //
   316  // This function is safe for concurrent access.
   317  func (b *BlockChain) CalcStakeVersionByHash(hash *chainhash.Hash) (uint32, error) {
   318  	b.chainLock.Lock()
   319  	version, err := b.calcStakeVersionByHash(hash)
   320  	b.chainLock.Unlock()
   321  	return version, err
   322  }