github.com/NebulousLabs/Sia@v1.3.7/modules/consensus/accept.go (about)

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