gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/consensus/accept.go (about)

     1  package consensus
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  
     9  	"time"
    10  
    11  	"gitlab.com/SiaPrime/SiaPrime/modules"
    12  	"gitlab.com/SiaPrime/SiaPrime/types"
    13  
    14  	bolt "github.com/coreos/bbolt"
    15  )
    16  
    17  var (
    18  	errDoSBlock        = errors.New("block is known to be invalid")
    19  	errInconsistentSet = errors.New("consensus set is not in a consistent state")
    20  	errNoBlockMap      = errors.New("block map is not in database")
    21  	errNonLinearChain  = errors.New("block set is not a contiguous chain")
    22  	errOrphan          = errors.New("block has no known parent")
    23  )
    24  
    25  // managedBroadcastBlock will broadcast a block to the consensus set's peers.
    26  func (cs *ConsensusSet) managedBroadcastBlock(b types.Block) {
    27  	// broadcast the block header to all peers
    28  	go cs.gateway.Broadcast("RelayHeader", b.Header(), cs.gateway.Peers())
    29  }
    30  
    31  // validateHeaderAndBlock does some early, low computation verification on the
    32  // block. Callers should not assume that validation will happen in a particular
    33  // order.
    34  func (cs *ConsensusSet) validateHeaderAndBlock(tx dbTx, b types.Block, id types.BlockID) (parent *processedBlock, err error) {
    35  	// Check if the block is a DoS block - a known invalid block that is expensive
    36  	// to validate.
    37  	_, exists := cs.dosBlocks[id]
    38  	if exists {
    39  		return nil, errDoSBlock
    40  	}
    41  
    42  	// Check if the block is already known.
    43  	blockMap := tx.Bucket(BlockMap)
    44  	if blockMap == nil {
    45  		return nil, errNoBlockMap
    46  	}
    47  	if blockMap.Get(id[:]) != nil {
    48  		return nil, modules.ErrBlockKnown
    49  	}
    50  
    51  	// Check for the parent.
    52  	parentID := b.ParentID
    53  	parentBytes := blockMap.Get(parentID[:])
    54  	if parentBytes == nil {
    55  		return nil, errOrphan
    56  	}
    57  	parent = new(processedBlock)
    58  	err = cs.marshaler.Unmarshal(parentBytes, parent)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	// Check that the timestamp is not too far in the past to be acceptable.
    63  	minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, parent)
    64  
    65  	// XXX debugging
    66  	var payoutSum types.Currency
    67  	for _, payout := range b.MinerPayouts {
    68  		//if payout.Value.IsZero() {
    69  		//        return false
    70  		//}
    71  		payoutSum = payoutSum.Add(payout.Value)
    72  	}
    73  	// cs.log.Debugln(fmt.Sprintf("block id: %s, #txs: %d, miner fees %s, miner subsidy (calculated by transaction fees): %s miner payout: %s\n", b.ID(), len(b.Transactions), b.CalculateMinerFees().String(), b.CalculateSubsidy(parent.Height+1).String(), payoutSum.String()))
    74  	// XXX debugging
    75  
    76  	err = cs.blockValidator.ValidateBlock(b, id, minTimestamp, parent.ChildTarget, parent.Height+1, cs.log)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	return parent, nil
    81  }
    82  
    83  // checkHeaderTarget returns true if the header's ID meets the given target.
    84  func checkHeaderTarget(h types.BlockHeader, target types.Target) bool {
    85  	blockHash := h.ID()
    86  	return bytes.Compare(target[:], blockHash[:]) >= 0
    87  }
    88  
    89  // validateHeader does some early, low computation verification on the header
    90  // to determine if the block should be downloaded. Callers should not assume
    91  // that validation will happen in a particular order.
    92  func (cs *ConsensusSet) validateHeader(tx dbTx, h types.BlockHeader) error {
    93  	// Check if the block is a DoS block - a known invalid block that is expensive
    94  	// to validate.
    95  	id := h.ID()
    96  	_, exists := cs.dosBlocks[id]
    97  	if exists {
    98  		return errDoSBlock
    99  	}
   100  
   101  	// Check if the block is already known.
   102  	blockMap := tx.Bucket(BlockMap)
   103  	if blockMap == nil {
   104  		return errNoBlockMap
   105  	}
   106  	if blockMap.Get(id[:]) != nil {
   107  		return modules.ErrBlockKnown
   108  	}
   109  
   110  	// Check for the parent.
   111  	parentID := h.ParentID
   112  	parentBytes := blockMap.Get(parentID[:])
   113  	if parentBytes == nil {
   114  		return errOrphan
   115  	}
   116  	var parent processedBlock
   117  	err := cs.marshaler.Unmarshal(parentBytes, &parent)
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	// Check that the nonce is a legal nonce.
   123  	if parent.Height+1 >= types.ASICHardforkHeight && binary.LittleEndian.Uint64(h.Nonce[:])%types.ASICHardforkFactor != 0 {
   124  		return errors.New("block does not meet nonce requirements")
   125  	}
   126  	// Check that the target of the new block is sufficient.
   127  	if !checkHeaderTarget(h, parent.ChildTarget) {
   128  		return modules.ErrBlockUnsolved
   129  	}
   130  
   131  	// TODO: check if the block is a non extending block once headers-first
   132  	// downloads are implemented.
   133  
   134  	// Check that the timestamp is not too far in the past to be acceptable.
   135  	minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, &parent)
   136  	if minTimestamp > h.Timestamp {
   137  		return errEarlyTimestamp
   138  	}
   139  
   140  	// Check if the block is in the extreme future. We make a distinction between
   141  	// future and extreme future because there is an assumption that by the time
   142  	// the extreme future arrives, this block will no longer be a part of the
   143  	// longest fork because it will have been ignored by all of the miners.
   144  	if h.Timestamp > types.CurrentTimestamp()+types.ExtremeFutureThreshold {
   145  		return errExtremeFutureTimestamp
   146  	}
   147  
   148  	// We do not check if the header is in the near future here, because we want
   149  	// to get the corresponding block as soon as possible, even if the block is in
   150  	// the near future.
   151  
   152  	return nil
   153  }
   154  
   155  // addBlockToTree inserts a block into the blockNode tree by adding it to its
   156  // parent's list of children. If the new blockNode is heavier than the current
   157  // node, the blockchain is forked to put the new block and its parents at the
   158  // tip. An error will be returned if block verification fails or if the block
   159  // does not extend the longest fork.
   160  //
   161  // addBlockToTree might need to modify the database while returning an error
   162  // on the block. Such errors are handled outside of the transaction by the
   163  // caller. Switching to a managed tx through bolt will make this complexity
   164  // unneeded.
   165  func (cs *ConsensusSet) addBlockToTree(tx *bolt.Tx, b types.Block, parent *processedBlock) (ce changeEntry, err error) {
   166  	// Prepare the child processed block associated with the parent block.
   167  	newNode := cs.newChild(tx, parent, b)
   168  
   169  	// Check whether the new node is part of a chain that is heavier than the
   170  	// current node. If not, return ErrNonExtending and don't fork the
   171  	// blockchain.
   172  	currentNode := currentProcessedBlock(tx)
   173  	if !newNode.heavierThan(currentNode) {
   174  		return changeEntry{}, modules.ErrNonExtendingBlock
   175  	}
   176  
   177  	// Fork the blockchain and put the new heaviest block at the tip of the
   178  	// chain.
   179  	var revertedBlocks, appliedBlocks []*processedBlock
   180  	revertedBlocks, appliedBlocks, err = cs.forkBlockchain(tx, newNode)
   181  	if err != nil {
   182  		return changeEntry{}, err
   183  	}
   184  	for _, rn := range revertedBlocks {
   185  		ce.RevertedBlocks = append(ce.RevertedBlocks, rn.Block.ID())
   186  	}
   187  	for _, an := range appliedBlocks {
   188  		ce.AppliedBlocks = append(ce.AppliedBlocks, an.Block.ID())
   189  	}
   190  	err = appendChangeLog(tx, ce)
   191  	if err != nil {
   192  		return changeEntry{}, err
   193  	}
   194  	return ce, nil
   195  }
   196  
   197  // threadedSleepOnFutureBlock will sleep until the timestamp of a future block
   198  // has arrived.
   199  //
   200  // TODO: An attacker can broadcast a future block multiple times, resulting in a
   201  // goroutine spinup for each future block.  Need to prevent that.
   202  //
   203  // TODO: An attacker could produce a very large number of future blocks,
   204  // consuming memory. Need to prevent that.
   205  func (cs *ConsensusSet) threadedSleepOnFutureBlock(b types.Block) {
   206  	// Add this thread to the threadgroup.
   207  	err := cs.tg.Add()
   208  	if err != nil {
   209  		return
   210  	}
   211  	defer cs.tg.Done()
   212  
   213  	// Perform a soft-sleep while we wait for the block to become valid.
   214  	select {
   215  	case <-cs.tg.StopChan():
   216  		return
   217  	case <-time.After(time.Duration(b.Timestamp-(types.CurrentTimestamp()+types.FutureThreshold)) * time.Second):
   218  		_, err := cs.managedAcceptBlocks([]types.Block{b})
   219  		if err != nil {
   220  			cs.log.Debugln("WARN: failed to accept a future block:", err)
   221  		}
   222  		cs.managedBroadcastBlock(b)
   223  	}
   224  }
   225  
   226  // managedAcceptBlocks will try to add blocks to the consensus set. If the
   227  // blocks do not extend the longest currently known chain, an error is
   228  // returned but the blocks are still kept in memory. If the blocks extend a fork
   229  // such that the fork becomes the longest currently known chain, the consensus
   230  // set will reorganize itself to recognize the new longest fork. Accepted
   231  // blocks are not relayed.
   232  //
   233  // Typically AcceptBlock should be used so that the accepted block is relayed.
   234  // This method is typically only be used when there would otherwise be multiple
   235  // consecutive calls to AcceptBlock with each successive call accepting the
   236  // child block of the previous call.
   237  func (cs *ConsensusSet) managedAcceptBlocks(blocks []types.Block) (blockchainExtended bool, err error) {
   238  	// Grab a lock on the consensus set.
   239  	cs.mu.Lock()
   240  	defer cs.mu.Unlock()
   241  
   242  	// Make sure that blocks are consecutive. Though this isn't a strict
   243  	// requirement, if blocks are not consecutive then it becomes a lot harder
   244  	// to maintain correcetness when adding multiple blocks in a single tx.
   245  	//
   246  	// This is the first time that IDs on the blocks have been computed.
   247  	blockIDs := make([]types.BlockID, 0, len(blocks))
   248  	for i := 0; i < len(blocks); i++ {
   249  		blockIDs = append(blockIDs, blocks[i].ID())
   250  		if i > 0 && blocks[i].ParentID != blockIDs[i-1] {
   251  			return false, errNonLinearChain
   252  		}
   253  	}
   254  
   255  	// Verify the headers for every block, throw out known blocks, and the
   256  	// invalid blocks (which includes the children of invalid blocks).
   257  	chainExtended := false
   258  	changes := make([]changeEntry, 0, len(blocks))
   259  	setErr := cs.db.Update(func(tx *bolt.Tx) error {
   260  		for i := 0; i < len(blocks); i++ {
   261  			// Start by checking the header of the block.
   262  			parent, err := cs.validateHeaderAndBlock(boltTxWrapper{tx}, blocks[i], blockIDs[i])
   263  			if err == modules.ErrBlockKnown {
   264  				// Skip over known blocks.
   265  				continue
   266  			}
   267  			if err == errFutureTimestamp {
   268  				// Queue the block to be tried again if it is a future block.
   269  				go cs.threadedSleepOnFutureBlock(blocks[i])
   270  			}
   271  			if err != nil {
   272  				return err
   273  			}
   274  
   275  			// Try adding the block to consensus.
   276  			changeEntry, err := cs.addBlockToTree(tx, blocks[i], parent)
   277  			if err == nil {
   278  				changes = append(changes, changeEntry)
   279  				chainExtended = true
   280  				var applied, reverted []string
   281  				for _, b := range changeEntry.AppliedBlocks {
   282  					applied = append(applied, b.String()[:6])
   283  				}
   284  				for _, b := range changeEntry.RevertedBlocks {
   285  					reverted = append(reverted, b.String()[:6])
   286  				}
   287  			}
   288  			if err == modules.ErrNonExtendingBlock {
   289  				err = nil
   290  			}
   291  			if err != nil {
   292  				return err
   293  			}
   294  			// Sanity check - we should never apply fewer blocks than we revert.
   295  			if len(changeEntry.AppliedBlocks) < len(changeEntry.RevertedBlocks) {
   296  				err := errors.New("after adding a change entry, there are more reverted blocks than applied ones")
   297  				cs.log.Severe(err)
   298  				return err
   299  			}
   300  		}
   301  		return nil
   302  	})
   303  	// TODO: should panic when "Blockchain database has run out of disk space"
   304  	//	if _, ok := setErr.(bolt.MmapError); ok {
   305  	//		cs.log.Println("ERROR: Bolt mmap failed:", setErr)
   306  	//		fmt.Println("Blockchain database has run out of disk space!")
   307  	//		os.Exit(1)
   308  	//	}
   309  	// NOTE: there is no bolt.MmapError in the github.com/coreos/bbolt package
   310  	if setErr != nil {
   311  		if len(changes) == 0 {
   312  			cs.log.Println("Consensus received an invalid block:", setErr)
   313  		} else {
   314  			fmt.Println("Received a partially valid block set.")
   315  			cs.log.Println("Consensus received a chain of blocks, where one was valid, but others were not:", setErr)
   316  		}
   317  		return false, setErr
   318  	}
   319  	// Stop here if the blocks did not extend the longest blockchain.
   320  	if !chainExtended {
   321  		return false, modules.ErrNonExtendingBlock
   322  	}
   323  	// Send any changes to subscribers.
   324  	for i := 0; i < len(changes); i++ {
   325  		cs.updateSubscribers(changes[i])
   326  	}
   327  	return chainExtended, nil
   328  }
   329  
   330  // AcceptBlock will try to add a block to the consensus set. If the block does
   331  // not extend the longest currently known chain, an error is returned but the
   332  // block is still kept in memory. If the block extends a fork such that the
   333  // fork becomes the longest currently known chain, the consensus set will
   334  // reorganize itself to recognize the new longest fork. If a block is accepted
   335  // without error, it will be relayed to all connected peers. This function
   336  // should only be called for new blocks.
   337  func (cs *ConsensusSet) AcceptBlock(b types.Block) error {
   338  	err := cs.tg.Add()
   339  	if err != nil {
   340  		return err
   341  	}
   342  	defer cs.tg.Done()
   343  
   344  	chainExtended, err := cs.managedAcceptBlocks([]types.Block{b})
   345  	if err != nil {
   346  		return err
   347  	}
   348  	if chainExtended {
   349  		cs.managedBroadcastBlock(b)
   350  	}
   351  	return nil
   352  }