github.com/Synthesix/Sia@v1.3.3-0.20180413141344-f863baeed3ca/modules/consensus/consensusset.go (about)

     1  package consensus
     2  
     3  // All changes to the consenuss set are made via diffs, specifically by calling
     4  // a commitDiff function. This means that future modifications (such as
     5  // replacing in-memory versions of the utxo set with on-disk versions of the
     6  // utxo set) should be relatively easy to verify for correctness. Modifying the
     7  // commitDiff functions will be sufficient.
     8  
     9  import (
    10  	"errors"
    11  
    12  	"github.com/Synthesix/Sia/encoding"
    13  	"github.com/Synthesix/Sia/modules"
    14  	"github.com/Synthesix/Sia/persist"
    15  	"github.com/Synthesix/Sia/sync"
    16  	"github.com/Synthesix/Sia/types"
    17  
    18  	"github.com/NebulousLabs/demotemutex"
    19  	"github.com/coreos/bbolt"
    20  )
    21  
    22  var (
    23  	errNilGateway = errors.New("cannot have a nil gateway as input")
    24  )
    25  
    26  // marshaler marshals objects into byte slices and unmarshals byte
    27  // slices into objects.
    28  type marshaler interface {
    29  	Marshal(interface{}) []byte
    30  	Unmarshal([]byte, interface{}) error
    31  }
    32  type stdMarshaler struct{}
    33  
    34  func (stdMarshaler) Marshal(v interface{}) []byte            { return encoding.Marshal(v) }
    35  func (stdMarshaler) Unmarshal(b []byte, v interface{}) error { return encoding.Unmarshal(b, v) }
    36  
    37  // The ConsensusSet is the object responsible for tracking the current status
    38  // of the blockchain. Broadly speaking, it is responsible for maintaining
    39  // consensus.  It accepts blocks and constructs a blockchain, forking when
    40  // necessary.
    41  type ConsensusSet struct {
    42  	// The gateway manages peer connections and keeps the consensus set
    43  	// synchronized to the rest of the network.
    44  	gateway modules.Gateway
    45  
    46  	// The block root contains the genesis block.
    47  	blockRoot processedBlock
    48  
    49  	// Subscribers to the consensus set will receive a changelog every time
    50  	// there is an update to the consensus set. At initialization, they receive
    51  	// all changes that they are missing.
    52  	//
    53  	// Memory: A consensus set typically has fewer than 10 subscribers, and
    54  	// subscription typically happens entirely at startup. This slice is
    55  	// unlikely to grow beyond 1kb, and cannot by manipulated by an attacker as
    56  	// the function of adding a subscriber should not be exposed.
    57  	subscribers []modules.ConsensusSetSubscriber
    58  
    59  	// dosBlocks are blocks that are invalid, but the invalidity is only
    60  	// discoverable during an expensive step of validation. These blocks are
    61  	// recorded to eliminate a DoS vector where an expensive-to-validate block
    62  	// is submitted to the consensus set repeatedly.
    63  	//
    64  	// TODO: dosBlocks needs to be moved into the database, and if there's some
    65  	// reason it can't be in THE database, it should be in a separate database.
    66  	// dosBlocks is an unbounded map that an attacker can manipulate, though
    67  	// iirc manipulations are expensive, to the tune of creating a blockchain
    68  	// PoW per DoS block (though the attacker could conceivably build off of
    69  	// the genesis block, meaning the PoW is not very expensive.
    70  	dosBlocks map[types.BlockID]struct{}
    71  
    72  	// checkingConsistency is a bool indicating whether or not a consistency
    73  	// check is in progress. The consistency check logic call itself, resulting
    74  	// in infinite loops. This bool prevents that while still allowing for full
    75  	// granularity consistency checks. Previously, consistency checks were only
    76  	// performed after a full reorg, but now they are performed after every
    77  	// block.
    78  	checkingConsistency bool
    79  
    80  	// synced is true if initial blockchain download has finished. It indicates
    81  	// whether the consensus set is synced with the network.
    82  	synced bool
    83  
    84  	// Interfaces to abstract the dependencies of the ConsensusSet.
    85  	marshaler       marshaler
    86  	blockRuleHelper blockRuleHelper
    87  	blockValidator  blockValidator
    88  
    89  	// Utilities
    90  	db         *persist.BoltDatabase
    91  	log        *persist.Logger
    92  	mu         demotemutex.DemoteMutex
    93  	persistDir string
    94  	tg         sync.ThreadGroup
    95  }
    96  
    97  // New returns a new ConsensusSet, containing at least the genesis block. If
    98  // there is an existing block database present in the persist directory, it
    99  // will be loaded.
   100  func New(gateway modules.Gateway, bootstrap bool, persistDir string) (*ConsensusSet, error) {
   101  	// Check for nil dependencies.
   102  	if gateway == nil {
   103  		return nil, errNilGateway
   104  	}
   105  
   106  	// Create the ConsensusSet object.
   107  	cs := &ConsensusSet{
   108  		gateway: gateway,
   109  
   110  		blockRoot: processedBlock{
   111  			Block:       types.GenesisBlock,
   112  			ChildTarget: types.RootTarget,
   113  			Depth:       types.RootDepth,
   114  
   115  			DiffsGenerated: true,
   116  		},
   117  
   118  		dosBlocks: make(map[types.BlockID]struct{}),
   119  
   120  		marshaler:       stdMarshaler{},
   121  		blockRuleHelper: stdBlockRuleHelper{},
   122  		blockValidator:  NewBlockValidator(),
   123  
   124  		persistDir: persistDir,
   125  	}
   126  
   127  	// Create the diffs for the genesis siafund outputs.
   128  	for i, siafundOutput := range types.GenesisBlock.Transactions[0].SiafundOutputs {
   129  		sfid := types.GenesisBlock.Transactions[0].SiafundOutputID(uint64(i))
   130  		sfod := modules.SiafundOutputDiff{
   131  			Direction:     modules.DiffApply,
   132  			ID:            sfid,
   133  			SiafundOutput: siafundOutput,
   134  		}
   135  		cs.blockRoot.SiafundOutputDiffs = append(cs.blockRoot.SiafundOutputDiffs, sfod)
   136  	}
   137  
   138  	// Initialize the consensus persistence structures.
   139  	err := cs.initPersist()
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	go func() {
   145  		// Sync with the network. Don't sync if we are testing because
   146  		// typically we don't have any mock peers to synchronize with in
   147  		// testing.
   148  		if bootstrap {
   149  			// We are in a virgin goroutine right now, so calling the threaded
   150  			// function without a goroutine is okay.
   151  			err = cs.threadedInitialBlockchainDownload()
   152  			if err != nil {
   153  				return
   154  			}
   155  		}
   156  
   157  		// threadedInitialBlockchainDownload will release the threadgroup 'Add'
   158  		// it was holding, so another needs to be grabbed to finish off this
   159  		// goroutine.
   160  		err = cs.tg.Add()
   161  		if err != nil {
   162  			return
   163  		}
   164  		defer cs.tg.Done()
   165  
   166  		// Register RPCs
   167  		gateway.RegisterRPC("SendBlocks", cs.rpcSendBlocks)
   168  		gateway.RegisterRPC("RelayHeader", cs.threadedRPCRelayHeader)
   169  		gateway.RegisterRPC("SendBlk", cs.rpcSendBlk)
   170  		gateway.RegisterConnectCall("SendBlocks", cs.threadedReceiveBlocks)
   171  		cs.tg.OnStop(func() {
   172  			cs.gateway.UnregisterRPC("SendBlocks")
   173  			cs.gateway.UnregisterRPC("RelayHeader")
   174  			cs.gateway.UnregisterRPC("SendBlk")
   175  			cs.gateway.UnregisterConnectCall("SendBlocks")
   176  		})
   177  
   178  		// Mark that we are synced with the network.
   179  		cs.mu.Lock()
   180  		cs.synced = true
   181  		cs.mu.Unlock()
   182  	}()
   183  
   184  	return cs, nil
   185  }
   186  
   187  // BlockAtHeight returns the block at a given height.
   188  func (cs *ConsensusSet) BlockAtHeight(height types.BlockHeight) (block types.Block, exists bool) {
   189  	_ = cs.db.View(func(tx *bolt.Tx) error {
   190  		id, err := getPath(tx, height)
   191  		if err != nil {
   192  			return err
   193  		}
   194  		pb, err := getBlockMap(tx, id)
   195  		if err != nil {
   196  			return err
   197  		}
   198  		block = pb.Block
   199  		exists = true
   200  		return nil
   201  	})
   202  	return block, exists
   203  }
   204  
   205  // BlockByID returns the block for a given BlockID.
   206  func (cs *ConsensusSet) BlockByID(id types.BlockID) (block types.Block, exists bool) {
   207  	_ = cs.db.View(func(tx *bolt.Tx) error {
   208  		pb, err := getBlockMap(tx, id)
   209  		if err != nil {
   210  			return err
   211  		}
   212  		block = pb.Block
   213  		exists = true
   214  		return nil
   215  	})
   216  	return block, exists
   217  }
   218  
   219  // ChildTarget returns the target for the child of a block.
   220  func (cs *ConsensusSet) ChildTarget(id types.BlockID) (target types.Target, exists bool) {
   221  	// A call to a closed database can cause undefined behavior.
   222  	err := cs.tg.Add()
   223  	if err != nil {
   224  		return types.Target{}, false
   225  	}
   226  	defer cs.tg.Done()
   227  
   228  	_ = cs.db.View(func(tx *bolt.Tx) error {
   229  		pb, err := getBlockMap(tx, id)
   230  		if err != nil {
   231  			return err
   232  		}
   233  		target = pb.ChildTarget
   234  		exists = true
   235  		return nil
   236  	})
   237  	return target, exists
   238  }
   239  
   240  // Close safely closes the block database.
   241  func (cs *ConsensusSet) Close() error {
   242  	return cs.tg.Stop()
   243  }
   244  
   245  // managedCurrentBlock returns the latest block in the heaviest known blockchain.
   246  func (cs *ConsensusSet) managedCurrentBlock() (block types.Block) {
   247  	cs.mu.RLock()
   248  	defer cs.mu.RUnlock()
   249  
   250  	_ = cs.db.View(func(tx *bolt.Tx) error {
   251  		pb := currentProcessedBlock(tx)
   252  		block = pb.Block
   253  		return nil
   254  	})
   255  	return block
   256  }
   257  
   258  // CurrentBlock returns the latest block in the heaviest known blockchain.
   259  func (cs *ConsensusSet) CurrentBlock() (block types.Block) {
   260  	// A call to a closed database can cause undefined behavior.
   261  	err := cs.tg.Add()
   262  	if err != nil {
   263  		return types.Block{}
   264  	}
   265  	defer cs.tg.Done()
   266  
   267  	// Block until a lock can be grabbed on the consensus set, indicating that
   268  	// all modules have received the most recent block. The lock is held so that
   269  	// there are no race conditions when trying to synchronize nodes.
   270  	cs.mu.Lock()
   271  	defer cs.mu.Unlock()
   272  
   273  	_ = cs.db.View(func(tx *bolt.Tx) error {
   274  		pb := currentProcessedBlock(tx)
   275  		block = pb.Block
   276  		return nil
   277  	})
   278  	return block
   279  }
   280  
   281  // Flush will block until the consensus set has finished all in-progress
   282  // routines.
   283  func (cs *ConsensusSet) Flush() error {
   284  	return cs.tg.Flush()
   285  }
   286  
   287  // Height returns the height of the consensus set.
   288  func (cs *ConsensusSet) Height() (height types.BlockHeight) {
   289  	// A call to a closed database can cause undefined behavior.
   290  	err := cs.tg.Add()
   291  	if err != nil {
   292  		return 0
   293  	}
   294  	defer cs.tg.Done()
   295  
   296  	// Block until a lock can be grabbed on the consensus set, indicating that
   297  	// all modules have received the most recent block. The lock is held so that
   298  	// there are no race conditions when trying to synchronize nodes.
   299  	cs.mu.Lock()
   300  	defer cs.mu.Unlock()
   301  
   302  	_ = cs.db.View(func(tx *bolt.Tx) error {
   303  		height = blockHeight(tx)
   304  		return nil
   305  	})
   306  	return height
   307  }
   308  
   309  // InCurrentPath returns true if the block presented is in the current path,
   310  // false otherwise.
   311  func (cs *ConsensusSet) InCurrentPath(id types.BlockID) (inPath bool) {
   312  	// A call to a closed database can cause undefined behavior.
   313  	err := cs.tg.Add()
   314  	if err != nil {
   315  		return false
   316  	}
   317  	defer cs.tg.Done()
   318  
   319  	_ = cs.db.View(func(tx *bolt.Tx) error {
   320  		pb, err := getBlockMap(tx, id)
   321  		if err != nil {
   322  			inPath = false
   323  			return nil
   324  		}
   325  		pathID, err := getPath(tx, pb.Height)
   326  		if err != nil {
   327  			inPath = false
   328  			return nil
   329  		}
   330  		inPath = pathID == id
   331  		return nil
   332  	})
   333  	return inPath
   334  }
   335  
   336  // MinimumValidChildTimestamp returns the earliest timestamp that the next block
   337  // can have in order for it to be considered valid.
   338  func (cs *ConsensusSet) MinimumValidChildTimestamp(id types.BlockID) (timestamp types.Timestamp, exists bool) {
   339  	// A call to a closed database can cause undefined behavior.
   340  	err := cs.tg.Add()
   341  	if err != nil {
   342  		return 0, false
   343  	}
   344  	defer cs.tg.Done()
   345  
   346  	// Error is not checked because it does not matter.
   347  	_ = cs.db.View(func(tx *bolt.Tx) error {
   348  		pb, err := getBlockMap(tx, id)
   349  		if err != nil {
   350  			return err
   351  		}
   352  		timestamp = cs.blockRuleHelper.minimumValidChildTimestamp(tx.Bucket(BlockMap), pb)
   353  		exists = true
   354  		return nil
   355  	})
   356  	return timestamp, exists
   357  }
   358  
   359  // StorageProofSegment returns the segment to be used in the storage proof for
   360  // a given file contract.
   361  func (cs *ConsensusSet) StorageProofSegment(fcid types.FileContractID) (index uint64, err error) {
   362  	// A call to a closed database can cause undefined behavior.
   363  	err = cs.tg.Add()
   364  	if err != nil {
   365  		return 0, err
   366  	}
   367  	defer cs.tg.Done()
   368  
   369  	_ = cs.db.View(func(tx *bolt.Tx) error {
   370  		index, err = storageProofSegment(tx, fcid)
   371  		return nil
   372  	})
   373  	return index, err
   374  }