github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/consensus/accept.go (about)

     1  package consensus
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"time"
     7  
     8  	"github.com/NebulousLabs/Sia/build"
     9  	"github.com/NebulousLabs/Sia/modules"
    10  	"github.com/NebulousLabs/Sia/types"
    11  
    12  	"github.com/NebulousLabs/bolt"
    13  )
    14  
    15  var (
    16  	errDoSBlock        = errors.New("block is known to be invalid")
    17  	errNoBlockMap      = errors.New("block map is not in database")
    18  	errInconsistentSet = errors.New("consensus set is not in a consistent state")
    19  	errOrphan          = errors.New("block has no known parent")
    20  )
    21  
    22  // validateHeaderAndBlock does some early, low computation verification on the
    23  // block. Callers should not assume that validation will happen in a particular
    24  // order.
    25  func (cs *ConsensusSet) validateHeaderAndBlock(tx dbTx, b types.Block) error {
    26  	// Check if the block is a DoS block - a known invalid block that is expensive
    27  	// to validate.
    28  	id := b.ID()
    29  	_, exists := cs.dosBlocks[id]
    30  	if exists {
    31  		return errDoSBlock
    32  	}
    33  
    34  	// Check if the block is already known.
    35  	blockMap := tx.Bucket(BlockMap)
    36  	if blockMap == nil {
    37  		return errNoBlockMap
    38  	}
    39  	if blockMap.Get(id[:]) != nil {
    40  		return modules.ErrBlockKnown
    41  	}
    42  
    43  	// Check for the parent.
    44  	parentID := b.ParentID
    45  	parentBytes := blockMap.Get(parentID[:])
    46  	if parentBytes == nil {
    47  		return errOrphan
    48  	}
    49  	var parent processedBlock
    50  	err := cs.marshaler.Unmarshal(parentBytes, &parent)
    51  	if err != nil {
    52  		return err
    53  	}
    54  	// Check that the timestamp is not too far in the past to be acceptable.
    55  	minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, &parent)
    56  
    57  	return cs.blockValidator.ValidateBlock(b, minTimestamp, parent.ChildTarget, parent.Height+1)
    58  }
    59  
    60  // checkHeaderTarget returns true if the header's ID meets the given target.
    61  func checkHeaderTarget(h types.BlockHeader, target types.Target) bool {
    62  	blockHash := h.ID()
    63  	return bytes.Compare(target[:], blockHash[:]) >= 0
    64  }
    65  
    66  // validateHeader does some early, low computation verification on the header
    67  // to determine if the block should be downloaded. Callers should not assume
    68  // that validation will happen in a particular order.
    69  func (cs *ConsensusSet) validateHeader(tx dbTx, h types.BlockHeader) error {
    70  	// Check if the block is a DoS block - a known invalid block that is expensive
    71  	// to validate.
    72  	id := h.ID()
    73  	_, exists := cs.dosBlocks[id]
    74  	if exists {
    75  		return errDoSBlock
    76  	}
    77  
    78  	// Check if the block is already known.
    79  	blockMap := tx.Bucket(BlockMap)
    80  	if blockMap == nil {
    81  		return errNoBlockMap
    82  	}
    83  	if blockMap.Get(id[:]) != nil {
    84  		return modules.ErrBlockKnown
    85  	}
    86  
    87  	// Check for the parent.
    88  	parentID := h.ParentID
    89  	parentBytes := blockMap.Get(parentID[:])
    90  	if parentBytes == nil {
    91  		return errOrphan
    92  	}
    93  	var parent processedBlock
    94  	err := cs.marshaler.Unmarshal(parentBytes, &parent)
    95  	if err != nil {
    96  		return err
    97  	}
    98  
    99  	// Check that the target of the new block is sufficient.
   100  	if !checkHeaderTarget(h, parent.ChildTarget) {
   101  		return modules.ErrBlockUnsolved
   102  	}
   103  
   104  	// TODO: check if the block is a non extending block once headers-first
   105  	// downloads are implemented.
   106  
   107  	// Check that the timestamp is not too far in the past to be acceptable.
   108  	minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, &parent)
   109  	if minTimestamp > h.Timestamp {
   110  		return errEarlyTimestamp
   111  	}
   112  
   113  	// Check if the block is in the extreme future. We make a distinction between
   114  	// future and extreme future because there is an assumption that by the time
   115  	// the extreme future arrives, this block will no longer be a part of the
   116  	// longest fork because it will have been ignored by all of the miners.
   117  	if h.Timestamp > types.CurrentTimestamp()+types.ExtremeFutureThreshold {
   118  		return errExtremeFutureTimestamp
   119  	}
   120  
   121  	// We do not check if the header is in the near future here, because we want
   122  	// to get the corresponding block as soon as possible, even if the block is in
   123  	// the near future.
   124  
   125  	return nil
   126  }
   127  
   128  // addBlockToTree inserts a block into the blockNode tree by adding it to its
   129  // parent's list of children. If the new blockNode is heavier than the current
   130  // node, the blockchain is forked to put the new block and its parents at the
   131  // tip. An error will be returned if block verification fails or if the block
   132  // does not extend the longest fork.
   133  //
   134  // addBlockToTree must use its own database update because it might need to
   135  // modify the database while returning an error on the block. To prevent error
   136  // tracking complexity, the error is handled inside the function so that 'nil'
   137  // can be appropriately returned by the database and the transaction can be
   138  // committed. Switching to a managed tx through bolt will make this complexity
   139  // unneeded.
   140  func (cs *ConsensusSet) addBlockToTree(b types.Block) (ce changeEntry, err error) {
   141  	var nonExtending bool
   142  	err = cs.db.Update(func(tx *bolt.Tx) error {
   143  		pb, err := getBlockMap(tx, b.ParentID)
   144  		if build.DEBUG && err != nil {
   145  			panic(err)
   146  		}
   147  		currentNode := currentProcessedBlock(tx)
   148  		newNode := cs.newChild(tx, pb, b)
   149  
   150  		// modules.ErrNonExtendingBlock should be returned if the block does
   151  		// not extend the current blockchain, however the changes from newChild
   152  		// should be committed (which means 'nil' must be returned). A flag is
   153  		// set to indicate that modules.ErrNonExtending should be returned.
   154  		nonExtending = !newNode.heavierThan(currentNode)
   155  		if nonExtending {
   156  			return nil
   157  		}
   158  		var revertedBlocks, appliedBlocks []*processedBlock
   159  		revertedBlocks, appliedBlocks, err = cs.forkBlockchain(tx, newNode)
   160  		if err != nil {
   161  			return err
   162  		}
   163  		for _, rn := range revertedBlocks {
   164  			ce.RevertedBlocks = append(ce.RevertedBlocks, rn.Block.ID())
   165  		}
   166  		for _, an := range appliedBlocks {
   167  			ce.AppliedBlocks = append(ce.AppliedBlocks, an.Block.ID())
   168  		}
   169  		// To have correct error handling, appendChangeLog must be called
   170  		// before appending to the in-memory changelog. If this call fails, the
   171  		// change is going to be reverted, but the in-memory changelog is not
   172  		// going to be reverted.
   173  		//
   174  		// Technically, if bolt fails for some other reason (such as a
   175  		// filesystem error), the in-memory changelog will be incorrect anyway.
   176  		// Restarting Sia will fix it. The in-memory changelog is being phased
   177  		// out.
   178  		err = appendChangeLog(tx, ce)
   179  		if err != nil {
   180  			return err
   181  		}
   182  		return nil
   183  	})
   184  	if err != nil {
   185  		return changeEntry{}, err
   186  	}
   187  	if nonExtending {
   188  		return changeEntry{}, modules.ErrNonExtendingBlock
   189  	}
   190  	return ce, nil
   191  }
   192  
   193  // managedAcceptBlock will try to add a block to the consensus set. If the
   194  // block does not extend the longest currently known chain, an error is
   195  // returned but the block is still kept in memory. If the block extends a fork
   196  // such that the fork becomes the longest currently known chain, the consensus
   197  // set will reorganize itself to recognize the new longest fork. Accepted
   198  // blocks are not relayed.
   199  //
   200  // Typically AcceptBlock should be used so that the accepted block is relayed.
   201  // This method is typically only be used when there would otherwise be multiple
   202  // consecutive calls to AcceptBlock with each successive call accepting the
   203  // child block of the previous call.
   204  func (cs *ConsensusSet) managedAcceptBlock(b types.Block) error {
   205  	// Grab a lock on the consensus set. Lock is demoted later in the function,
   206  	// failure to unlock before returning an error will cause a deadlock.
   207  	cs.mu.Lock()
   208  
   209  	// Start verification inside of a bolt View tx.
   210  	err := cs.db.View(func(tx *bolt.Tx) error {
   211  		// Do not accept a block if the database is inconsistent.
   212  		if inconsistencyDetected(tx) {
   213  			return errInconsistentSet
   214  		}
   215  
   216  		// Do some relatively inexpensive checks to validate the header and block.
   217  		// Validation generally occurs in the order of least expensive validation
   218  		// first.
   219  		err := cs.validateHeaderAndBlock(boltTxWrapper{tx}, b)
   220  		if err != nil {
   221  			// If the block is in the near future, but too far to be acceptable, then
   222  			// save the block and add it to the consensus set after it is no longer
   223  			// too far in the future.
   224  			//
   225  			// TODO: an attacker could mine many blocks off the genesis block all in the
   226  			// future and we would spawn a goroutine per each block. To fix this, either
   227  			// ban peers that send lots of future blocks and stop spawning goroutines
   228  			// after we are already waiting on a large number of future blocks.
   229  			//
   230  			// TODO: an attacker could broadcast a future block many times and we would
   231  			// spawn a goroutine for each broadcast. To fix this we should create a
   232  			// cache of future blocks, like we already do for DoS blocks, and only spawn
   233  			// a goroutine if we haven't already spawned one for that block. To limit
   234  			// the size of the cache of future blocks, make it a constant size (say 50)
   235  			// over which we would evict the block furthest in the future before adding
   236  			// a new block to the cache.
   237  			if err == errFutureTimestamp {
   238  				go func() {
   239  					time.Sleep(time.Duration(b.Timestamp-(types.CurrentTimestamp()+types.FutureThreshold)) * time.Second)
   240  					err := cs.AcceptBlock(b)
   241  					if err != nil {
   242  						cs.log.Debugln("WARN: failed to accept a future block:", err)
   243  					}
   244  				}()
   245  			}
   246  			return err
   247  		}
   248  		return nil
   249  	})
   250  	if err != nil {
   251  		cs.mu.Unlock()
   252  		return err
   253  	}
   254  
   255  	// Try adding the block to the block tree. This call will perform
   256  	// verification on the block before adding the block to the block tree. An
   257  	// error is returned if verification fails or if the block does not extend
   258  	// the longest fork.
   259  	changeEntry, err := cs.addBlockToTree(b)
   260  	if err != nil {
   261  		cs.mu.Unlock()
   262  		return err
   263  	}
   264  	// If appliedBlocks is 0, revertedBlocks will also be 0.
   265  	if build.DEBUG && len(changeEntry.AppliedBlocks) == 0 && len(changeEntry.RevertedBlocks) != 0 {
   266  		panic("appliedBlocks and revertedBlocks are mismatched!")
   267  	}
   268  
   269  	// Updates complete, demote the lock.
   270  	cs.mu.Demote()
   271  	defer cs.mu.DemotedUnlock()
   272  	if len(changeEntry.AppliedBlocks) > 0 {
   273  		cs.readlockUpdateSubscribers(changeEntry)
   274  	}
   275  	return nil
   276  }
   277  
   278  // AcceptBlock will try to add a block to the consensus set. If the block does
   279  // not extend the longest currently known chain, an error is returned but the
   280  // block is still kept in memory. If the block extends a fork such that the
   281  // fork becomes the longest currently known chain, the consensus set will
   282  // reorganize itself to recognize the new longest fork. If a block is accepted
   283  // without error, it will be relayed to all connected peers. This function
   284  // should only be called for new blocks.
   285  func (cs *ConsensusSet) AcceptBlock(b types.Block) error {
   286  	err := cs.managedAcceptBlock(b)
   287  	if err != nil {
   288  		return err
   289  	}
   290  	// COMPATv0.5.1 - broadcast the block to all peers <= v0.5.1 and block header to all peers > v0.5.1.
   291  	var relayBlockPeers, relayHeaderPeers []modules.Peer
   292  	for _, p := range cs.gateway.Peers() {
   293  		if build.VersionCmp(p.Version, "0.5.1") <= 0 {
   294  			relayBlockPeers = append(relayBlockPeers, p)
   295  		} else {
   296  			relayHeaderPeers = append(relayHeaderPeers, p)
   297  		}
   298  	}
   299  	go cs.gateway.Broadcast("RelayBlock", b, relayBlockPeers)
   300  	go cs.gateway.Broadcast("RelayHeader", b.Header(), relayHeaderPeers)
   301  	return nil
   302  }