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