mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 10:56:47 +00:00
This udpates the blockchain module to use the lastest module major versions. While here, it also corrects a few typos and updates some test function names to more accurately reflect their purpose. The updated direct dependencies are as follows: - github.com/decred/dcrd/blockchain/stake/v2@v2.0.1 - github.com/decred/dcrd/chaincfg/v2@v2.2.0 - github.com/decred/dcrd/database/v2@v2.0.0 - github.com/decred/dcrd/dcrutil/v2@v2.0.0 - github.com/decred/dcrd/txscript/v2@v2.0.0
499 lines
17 KiB
Go
499 lines
17 KiB
Go
// Copyright (c) 2013-2017 The btcsuite developers
|
|
// Copyright (c) 2018-2019 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package blockchain
|
|
|
|
import (
|
|
"bytes"
|
|
"math/big"
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/decred/dcrd/blockchain/stake/v2"
|
|
"github.com/decred/dcrd/blockchain/standalone"
|
|
"github.com/decred/dcrd/chaincfg/chainhash"
|
|
"github.com/decred/dcrd/database/v2"
|
|
"github.com/decred/dcrd/wire"
|
|
)
|
|
|
|
// blockStatus is a bit field representing the validation state of the block.
|
|
type blockStatus byte
|
|
|
|
// The following constants specify possible status bit flags for a block.
|
|
//
|
|
// NOTE: This section specifically does not use iota since the block status is
|
|
// serialized and must be stable for long-term storage.
|
|
const (
|
|
// statusNone indicates that the block has no validation state flags set.
|
|
statusNone blockStatus = 0
|
|
|
|
// statusDataStored indicates that the block's payload is stored on disk.
|
|
statusDataStored blockStatus = 1 << 0
|
|
|
|
// statusValid indicates that the block has been fully validated. It also
|
|
// means that all of its ancestors have also been validated.
|
|
statusValid blockStatus = 1 << 1
|
|
|
|
// statusValidateFailed indicates that the block has failed validation.
|
|
statusValidateFailed blockStatus = 1 << 2
|
|
|
|
// statusInvalidAncestor indicates that one of the ancestors of the block
|
|
// has failed validation, thus the block is also invalid.
|
|
statusInvalidAncestor blockStatus = 1 << 3
|
|
)
|
|
|
|
// HaveData returns whether the full block data is stored in the database. This
|
|
// will return false for a block node where only the header is downloaded or
|
|
// stored.
|
|
func (status blockStatus) HaveData() bool {
|
|
return status&statusDataStored != 0
|
|
}
|
|
|
|
// KnownValid returns whether the block is known to be valid. This will return
|
|
// false for a valid block that has not been fully validated yet.
|
|
func (status blockStatus) KnownValid() bool {
|
|
return status&statusValid != 0
|
|
}
|
|
|
|
// KnownInvalid returns whether the block is known to be invalid. This will
|
|
// return false for invalid blocks that have not been proven invalid yet.
|
|
func (status blockStatus) KnownInvalid() bool {
|
|
return status&(statusValidateFailed|statusInvalidAncestor) != 0
|
|
}
|
|
|
|
// blockNode represents a block within the block chain and is primarily used to
|
|
// aid in selecting the best chain to be the main chain. The main chain is
|
|
// stored into the block database.
|
|
type blockNode struct {
|
|
// NOTE: Additions, deletions, or modifications to the order of the
|
|
// definitions in this struct should not be changed without considering
|
|
// how it affects alignment on 64-bit platforms. The current order is
|
|
// specifically crafted to result in minimal padding. There will be
|
|
// hundreds of thousands of these in memory, so a few extra bytes of
|
|
// padding adds up.
|
|
|
|
// parent is the parent block for this node.
|
|
parent *blockNode
|
|
|
|
// hash is the hash of the block this node represents.
|
|
hash chainhash.Hash
|
|
|
|
// workSum is the total amount of work in the chain up to and including
|
|
// this node.
|
|
workSum *big.Int
|
|
|
|
// Some fields from block headers to aid in best chain selection and
|
|
// reconstructing headers from memory. These must be treated as
|
|
// immutable and are intentionally ordered to avoid padding on 64-bit
|
|
// platforms.
|
|
height int64
|
|
voteBits uint16
|
|
finalState [6]byte
|
|
blockVersion int32
|
|
voters uint16
|
|
freshStake uint8
|
|
revocations uint8
|
|
poolSize uint32
|
|
bits uint32
|
|
sbits int64
|
|
timestamp int64
|
|
merkleRoot chainhash.Hash
|
|
stakeRoot chainhash.Hash
|
|
blockSize uint32
|
|
nonce uint32
|
|
extraData [32]byte
|
|
stakeVersion uint32
|
|
|
|
// status is a bitfield representing the validation state of the block.
|
|
// This field, unlike the other fields, may be changed after the block
|
|
// node is created, so it must only be accessed or updated using the
|
|
// concurrent-safe NodeStatus, SetStatusFlags, and UnsetStatusFlags
|
|
// methods on blockIndex once the node has been added to the index.
|
|
status blockStatus
|
|
|
|
// stakeNode contains all the consensus information required for the
|
|
// staking system. The node also caches information required to add or
|
|
// remove stake nodes, so that the stake node itself may be pruneable
|
|
// to save memory while maintaining high throughput efficiency for the
|
|
// evaluation of sidechains.
|
|
stakeNode *stake.Node
|
|
newTickets []chainhash.Hash
|
|
ticketsVoted []chainhash.Hash
|
|
ticketsRevoked []chainhash.Hash
|
|
|
|
// Keep track of all vote version and bits in this block.
|
|
votes []stake.VoteVersionTuple
|
|
}
|
|
|
|
// initBlockNode initializes a block node from the given header, initialization
|
|
// vector for the ticket lottery, and parent node. The workSum is calculated
|
|
// based on the parent, or, in the case no parent is provided, it will just be
|
|
// the work for the passed block.
|
|
//
|
|
// This function is NOT safe for concurrent access. It must only be called when
|
|
// initially creating a node.
|
|
func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parent *blockNode) {
|
|
*node = blockNode{
|
|
hash: blockHeader.BlockHash(),
|
|
workSum: standalone.CalcWork(blockHeader.Bits),
|
|
height: int64(blockHeader.Height),
|
|
blockVersion: blockHeader.Version,
|
|
voteBits: blockHeader.VoteBits,
|
|
finalState: blockHeader.FinalState,
|
|
voters: blockHeader.Voters,
|
|
freshStake: blockHeader.FreshStake,
|
|
poolSize: blockHeader.PoolSize,
|
|
bits: blockHeader.Bits,
|
|
sbits: blockHeader.SBits,
|
|
timestamp: blockHeader.Timestamp.Unix(),
|
|
merkleRoot: blockHeader.MerkleRoot,
|
|
stakeRoot: blockHeader.StakeRoot,
|
|
revocations: blockHeader.Revocations,
|
|
blockSize: blockHeader.Size,
|
|
nonce: blockHeader.Nonce,
|
|
extraData: blockHeader.ExtraData,
|
|
stakeVersion: blockHeader.StakeVersion,
|
|
}
|
|
if parent != nil {
|
|
node.parent = parent
|
|
node.workSum = node.workSum.Add(parent.workSum, node.workSum)
|
|
}
|
|
}
|
|
|
|
// newBlockNode returns a new block node for the given block header and parent
|
|
// node. The workSum is calculated based on the parent, or, in the case no
|
|
// parent is provided, it will just be the work for the passed block.
|
|
func newBlockNode(blockHeader *wire.BlockHeader, parent *blockNode) *blockNode {
|
|
var node blockNode
|
|
initBlockNode(&node, blockHeader, parent)
|
|
return &node
|
|
}
|
|
|
|
// Header constructs a block header from the node and returns it.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (node *blockNode) Header() wire.BlockHeader {
|
|
// No lock is needed because all accessed fields are immutable.
|
|
prevHash := zeroHash
|
|
if node.parent != nil {
|
|
prevHash = &node.parent.hash
|
|
}
|
|
return wire.BlockHeader{
|
|
Version: node.blockVersion,
|
|
PrevBlock: *prevHash,
|
|
MerkleRoot: node.merkleRoot,
|
|
StakeRoot: node.stakeRoot,
|
|
VoteBits: node.voteBits,
|
|
FinalState: node.finalState,
|
|
Voters: node.voters,
|
|
FreshStake: node.freshStake,
|
|
Revocations: node.revocations,
|
|
PoolSize: node.poolSize,
|
|
Bits: node.bits,
|
|
SBits: node.sbits,
|
|
Height: uint32(node.height),
|
|
Size: node.blockSize,
|
|
Timestamp: time.Unix(node.timestamp, 0),
|
|
Nonce: node.nonce,
|
|
ExtraData: node.extraData,
|
|
StakeVersion: node.stakeVersion,
|
|
}
|
|
}
|
|
|
|
// lotteryIV returns the initialization vector for the deterministic PRNG used
|
|
// to determine winning tickets.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (node *blockNode) lotteryIV() chainhash.Hash {
|
|
// Serialize the block header for use in calculating the initialization
|
|
// vector for the ticket lottery. The only way this can fail is if the
|
|
// process is out of memory in which case it would panic anyways, so
|
|
// although panics are generally frowned upon in package code, it is
|
|
// acceptable here.
|
|
buf := bytes.NewBuffer(make([]byte, 0, wire.MaxBlockHeaderPayload))
|
|
header := node.Header()
|
|
if err := header.Serialize(buf); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return stake.CalcHash256PRNGIV(buf.Bytes())
|
|
}
|
|
|
|
// populateTicketInfo sets pruneable ticket information in the provided block
|
|
// node.
|
|
//
|
|
// This function is NOT safe for concurrent access. It must only be called when
|
|
// initially creating a node or when protected by the chain lock.
|
|
func (node *blockNode) populateTicketInfo(spentTickets *stake.SpentTicketsInBlock) {
|
|
node.ticketsVoted = spentTickets.VotedTickets
|
|
node.ticketsRevoked = spentTickets.RevokedTickets
|
|
node.votes = spentTickets.Votes
|
|
}
|
|
|
|
// Ancestor returns the ancestor block node at the provided height by following
|
|
// the chain backwards from this node. The returned block will be nil when a
|
|
// height is requested that is after the height of the passed node or is less
|
|
// than zero.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (node *blockNode) Ancestor(height int64) *blockNode {
|
|
if height < 0 || height > node.height {
|
|
return nil
|
|
}
|
|
|
|
n := node
|
|
for ; n != nil && n.height != height; n = n.parent {
|
|
// Intentionally left blank
|
|
}
|
|
|
|
return n
|
|
}
|
|
|
|
// RelativeAncestor returns the ancestor block node a relative 'distance' blocks
|
|
// before this node. This is equivalent to calling Ancestor with the node's
|
|
// height minus provided distance.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (node *blockNode) RelativeAncestor(distance int64) *blockNode {
|
|
return node.Ancestor(node.height - distance)
|
|
}
|
|
|
|
// CalcPastMedianTime calculates the median time of the previous few blocks
|
|
// prior to, and including, the block node.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (node *blockNode) CalcPastMedianTime() time.Time {
|
|
// Create a slice of the previous few block timestamps used to calculate
|
|
// the median per the number defined by the constant medianTimeBlocks.
|
|
timestamps := make([]int64, medianTimeBlocks)
|
|
numNodes := 0
|
|
iterNode := node
|
|
for i := 0; i < medianTimeBlocks && iterNode != nil; i++ {
|
|
timestamps[i] = iterNode.timestamp
|
|
numNodes++
|
|
|
|
iterNode = iterNode.parent
|
|
}
|
|
|
|
// Prune the slice to the actual number of available timestamps which
|
|
// will be fewer than desired near the beginning of the block chain
|
|
// and sort them.
|
|
timestamps = timestamps[:numNodes]
|
|
sort.Sort(timeSorter(timestamps))
|
|
|
|
// NOTE: The consensus rules incorrectly calculate the median for even
|
|
// numbers of blocks. A true median averages the middle two elements
|
|
// for a set with an even number of elements in it. Since the constant
|
|
// for the previous number of blocks to be used is odd, this is only an
|
|
// issue for a few blocks near the beginning of the chain. I suspect
|
|
// this is an optimization even though the result is slightly wrong for
|
|
// a few of the first blocks since after the first few blocks, there
|
|
// will always be an odd number of blocks in the set per the constant.
|
|
//
|
|
// This code follows suit to ensure the same rules are used, however, be
|
|
// aware that should the medianTimeBlocks constant ever be changed to an
|
|
// even number, this code will be wrong.
|
|
medianTimestamp := timestamps[numNodes/2]
|
|
return time.Unix(medianTimestamp, 0)
|
|
}
|
|
|
|
// blockIndex provides facilities for keeping track of an in-memory index of the
|
|
// block chain. Although the name block chain suggests a single chain of
|
|
// blocks, it is actually a tree-shaped structure where any node can have
|
|
// multiple children. However, there can only be one active branch which does
|
|
// indeed form a chain from the tip all the way back to the genesis block.
|
|
type blockIndex struct {
|
|
// The following fields are set when the instance is created and can't
|
|
// be changed afterwards, so there is no need to protect them with a
|
|
// separate mutex.
|
|
db database.DB
|
|
|
|
// These following fields are protected by the embedded mutex.
|
|
//
|
|
// index contains an entry for every known block tracked by the block
|
|
// index.
|
|
//
|
|
// modified contains an entry for all nodes that have been modified
|
|
// since the last time the index was flushed to disk.
|
|
//
|
|
// chainTips contains an entry with the tip of all known side chains.
|
|
sync.RWMutex
|
|
index map[chainhash.Hash]*blockNode
|
|
modified map[*blockNode]struct{}
|
|
chainTips map[int64][]*blockNode
|
|
}
|
|
|
|
// newBlockIndex returns a new empty instance of a block index. The index will
|
|
// be dynamically populated as block nodes are loaded from the database and
|
|
// manually added.
|
|
func newBlockIndex(db database.DB) *blockIndex {
|
|
return &blockIndex{
|
|
db: db,
|
|
index: make(map[chainhash.Hash]*blockNode),
|
|
modified: make(map[*blockNode]struct{}),
|
|
chainTips: make(map[int64][]*blockNode),
|
|
}
|
|
}
|
|
|
|
// HaveBlock returns whether or not the block index contains the provided hash
|
|
// and the block data is available.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (bi *blockIndex) HaveBlock(hash *chainhash.Hash) bool {
|
|
bi.RLock()
|
|
node := bi.index[*hash]
|
|
hasBlock := node != nil && node.status.HaveData()
|
|
bi.RUnlock()
|
|
return hasBlock
|
|
}
|
|
|
|
// addNode adds the provided node to the block index. Duplicate entries are not
|
|
// checked so it is up to caller to avoid adding them.
|
|
//
|
|
// This function MUST be called with the block index lock held (for writes).
|
|
func (bi *blockIndex) addNode(node *blockNode) {
|
|
bi.index[node.hash] = node
|
|
|
|
// Since the block index does not support nodes that do not connect to
|
|
// an existing node (except the genesis block), all new nodes are either
|
|
// extending an existing chain or are on a side chain, but in either
|
|
// case, are a new chain tip. In the case the node is extending a
|
|
// chain, the parent is no longer a tip.
|
|
bi.addChainTip(node)
|
|
if node.parent != nil {
|
|
bi.removeChainTip(node.parent)
|
|
}
|
|
}
|
|
|
|
// AddNode adds the provided node to the block index and marks it as modified.
|
|
// Duplicate entries are not checked so it is up to caller to avoid adding them.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (bi *blockIndex) AddNode(node *blockNode) {
|
|
bi.Lock()
|
|
bi.addNode(node)
|
|
bi.modified[node] = struct{}{}
|
|
bi.Unlock()
|
|
}
|
|
|
|
// addChainTip adds the passed block node as a new chain tip.
|
|
//
|
|
// This function MUST be called with the block index lock held (for writes).
|
|
func (bi *blockIndex) addChainTip(tip *blockNode) {
|
|
bi.chainTips[tip.height] = append(bi.chainTips[tip.height], tip)
|
|
}
|
|
|
|
// removeChainTip removes the passed block node from the available chain tips.
|
|
//
|
|
// This function MUST be called with the block index lock held (for writes).
|
|
func (bi *blockIndex) removeChainTip(tip *blockNode) {
|
|
nodes := bi.chainTips[tip.height]
|
|
for i, n := range nodes {
|
|
if n == tip {
|
|
copy(nodes[i:], nodes[i+1:])
|
|
nodes[len(nodes)-1] = nil
|
|
nodes = nodes[:len(nodes)-1]
|
|
break
|
|
}
|
|
}
|
|
|
|
// Either update the map entry for the height with the remaining nodes
|
|
// or remove it altogether if there are no more nodes left.
|
|
if len(nodes) == 0 {
|
|
delete(bi.chainTips, tip.height)
|
|
} else {
|
|
bi.chainTips[tip.height] = nodes
|
|
}
|
|
}
|
|
|
|
// lookupNode returns the block node identified by the provided hash. It will
|
|
// return nil if there is no entry for the hash.
|
|
//
|
|
// This function MUST be called with the block index lock held (for reads).
|
|
func (bi *blockIndex) lookupNode(hash *chainhash.Hash) *blockNode {
|
|
return bi.index[*hash]
|
|
}
|
|
|
|
// LookupNode returns the block node identified by the provided hash. It will
|
|
// return nil if there is no entry for the hash.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (bi *blockIndex) LookupNode(hash *chainhash.Hash) *blockNode {
|
|
bi.RLock()
|
|
node := bi.lookupNode(hash)
|
|
bi.RUnlock()
|
|
return node
|
|
}
|
|
|
|
// NodeStatus returns the status associated with the provided node.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (bi *blockIndex) NodeStatus(node *blockNode) blockStatus {
|
|
bi.RLock()
|
|
status := node.status
|
|
bi.RUnlock()
|
|
return status
|
|
}
|
|
|
|
// SetStatusFlags sets the provided status flags for the given block node
|
|
// regardless of their previous state. It does not unset any flags.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (bi *blockIndex) SetStatusFlags(node *blockNode, flags blockStatus) {
|
|
bi.Lock()
|
|
origStatus := node.status
|
|
node.status |= flags
|
|
if node.status != origStatus {
|
|
bi.modified[node] = struct{}{}
|
|
}
|
|
bi.Unlock()
|
|
}
|
|
|
|
// UnsetStatusFlags unsets the provided status flags for the given block node
|
|
// regardless of their previous state.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (bi *blockIndex) UnsetStatusFlags(node *blockNode, flags blockStatus) {
|
|
bi.Lock()
|
|
origStatus := node.status
|
|
node.status &^= flags
|
|
if node.status != origStatus {
|
|
bi.modified[node] = struct{}{}
|
|
}
|
|
bi.Unlock()
|
|
}
|
|
|
|
// flush writes all of the modified block nodes to the database and clears the
|
|
// set of modified nodes if it succeeds.
|
|
func (bi *blockIndex) flush() error {
|
|
// Nothing to flush if there are no modified nodes.
|
|
bi.Lock()
|
|
if len(bi.modified) == 0 {
|
|
bi.Unlock()
|
|
return nil
|
|
}
|
|
|
|
// Write all of the nodes in the set of modified nodes to the database.
|
|
err := bi.db.Update(func(dbTx database.Tx) error {
|
|
for node := range bi.modified {
|
|
err := dbPutBlockNode(dbTx, node)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
bi.Unlock()
|
|
return err
|
|
}
|
|
|
|
// Clear the set of modified nodes.
|
|
bi.modified = make(map[*blockNode]struct{})
|
|
bi.Unlock()
|
|
return nil
|
|
}
|