github.com/NebulousLabs/Sia@v1.3.7/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/NebulousLabs/Sia/encoding"
    13  	"github.com/NebulousLabs/Sia/modules"
    14  	"github.com/NebulousLabs/Sia/persist"
    15  	siasync "github.com/NebulousLabs/Sia/sync"
    16  	"github.com/NebulousLabs/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  	staticDeps modules.Dependencies
    92  	log        *persist.Logger
    93  	mu         demotemutex.DemoteMutex
    94  	persistDir string
    95  	tg         siasync.ThreadGroup
    96  }
    97  
    98  // New returns a new ConsensusSet, containing at least the genesis block. If
    99  // there is an existing block database present in the persist directory, it
   100  // will be loaded.
   101  func New(gateway modules.Gateway, bootstrap bool, persistDir string) (*ConsensusSet, error) {
   102  	return NewCustomConsensusSet(gateway, bootstrap, persistDir, modules.ProdDependencies)
   103  }
   104  
   105  // NewCustomConsensusSet returns a new ConsensusSet, containing at least the genesis block. If
   106  // there is an existing block database present in the persist directory, it
   107  // will be loaded.
   108  func NewCustomConsensusSet(gateway modules.Gateway, bootstrap bool, persistDir string, deps modules.Dependencies) (*ConsensusSet, error) {
   109  	// Check for nil dependencies.
   110  	if gateway == nil {
   111  		return nil, errNilGateway
   112  	}
   113  
   114  	// Create the ConsensusSet object.
   115  	cs := &ConsensusSet{
   116  		gateway: gateway,
   117  
   118  		blockRoot: processedBlock{
   119  			Block:       types.GenesisBlock,
   120  			ChildTarget: types.RootTarget,
   121  			Depth:       types.RootDepth,
   122  
   123  			DiffsGenerated: true,
   124  		},
   125  
   126  		dosBlocks: make(map[types.BlockID]struct{}),
   127  
   128  		marshaler:       stdMarshaler{},
   129  		blockRuleHelper: stdBlockRuleHelper{},
   130  		blockValidator:  NewBlockValidator(),
   131  
   132  		staticDeps: deps,
   133  		persistDir: persistDir,
   134  	}
   135  
   136  	// Create the diffs for the genesis siafund outputs.
   137  	for i, siafundOutput := range types.GenesisBlock.Transactions[0].SiafundOutputs {
   138  		sfid := types.GenesisBlock.Transactions[0].SiafundOutputID(uint64(i))
   139  		sfod := modules.SiafundOutputDiff{
   140  			Direction:     modules.DiffApply,
   141  			ID:            sfid,
   142  			SiafundOutput: siafundOutput,
   143  		}
   144  		cs.blockRoot.SiafundOutputDiffs = append(cs.blockRoot.SiafundOutputDiffs, sfod)
   145  	}
   146  
   147  	// Initialize the consensus persistence structures.
   148  	err := cs.initPersist()
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	go func() {
   154  		// Sync with the network. Don't sync if we are testing because
   155  		// typically we don't have any mock peers to synchronize with in
   156  		// testing.
   157  		if bootstrap {
   158  			// We are in a virgin goroutine right now, so calling the threaded
   159  			// function without a goroutine is okay.
   160  			err = cs.threadedInitialBlockchainDownload()
   161  			if err != nil {
   162  				return
   163  			}
   164  		}
   165  
   166  		// threadedInitialBlockchainDownload will release the threadgroup 'Add'
   167  		// it was holding, so another needs to be grabbed to finish off this
   168  		// goroutine.
   169  		err = cs.tg.Add()
   170  		if err != nil {
   171  			return
   172  		}
   173  		defer cs.tg.Done()
   174  
   175  		// Register RPCs
   176  		gateway.RegisterRPC("SendBlocks", cs.rpcSendBlocks)
   177  		gateway.RegisterRPC("RelayHeader", cs.threadedRPCRelayHeader)
   178  		gateway.RegisterRPC("SendBlk", cs.rpcSendBlk)
   179  		gateway.RegisterConnectCall("SendBlocks", cs.threadedReceiveBlocks)
   180  		cs.tg.OnStop(func() {
   181  			cs.gateway.UnregisterRPC("SendBlocks")
   182  			cs.gateway.UnregisterRPC("RelayHeader")
   183  			cs.gateway.UnregisterRPC("SendBlk")
   184  			cs.gateway.UnregisterConnectCall("SendBlocks")
   185  		})
   186  
   187  		// Mark that we are synced with the network.
   188  		cs.mu.Lock()
   189  		cs.synced = true
   190  		cs.mu.Unlock()
   191  	}()
   192  
   193  	return cs, nil
   194  }
   195  
   196  // BlockAtHeight returns the block at a given height.
   197  func (cs *ConsensusSet) BlockAtHeight(height types.BlockHeight) (block types.Block, exists bool) {
   198  	_ = cs.db.View(func(tx *bolt.Tx) error {
   199  		id, err := getPath(tx, height)
   200  		if err != nil {
   201  			return err
   202  		}
   203  		pb, err := getBlockMap(tx, id)
   204  		if err != nil {
   205  			return err
   206  		}
   207  		block = pb.Block
   208  		exists = true
   209  		return nil
   210  	})
   211  	return block, exists
   212  }
   213  
   214  // BlockByID returns the block for a given BlockID.
   215  func (cs *ConsensusSet) BlockByID(id types.BlockID) (block types.Block, height types.BlockHeight, exists bool) {
   216  	_ = cs.db.View(func(tx *bolt.Tx) error {
   217  		pb, err := getBlockMap(tx, id)
   218  		if err != nil {
   219  			return err
   220  		}
   221  		block = pb.Block
   222  		height = pb.Height
   223  		exists = true
   224  		return nil
   225  	})
   226  	return block, height, exists
   227  }
   228  
   229  // ChildTarget returns the target for the child of a block.
   230  func (cs *ConsensusSet) ChildTarget(id types.BlockID) (target types.Target, exists bool) {
   231  	// A call to a closed database can cause undefined behavior.
   232  	err := cs.tg.Add()
   233  	if err != nil {
   234  		return types.Target{}, false
   235  	}
   236  	defer cs.tg.Done()
   237  
   238  	_ = cs.db.View(func(tx *bolt.Tx) error {
   239  		pb, err := getBlockMap(tx, id)
   240  		if err != nil {
   241  			return err
   242  		}
   243  		target = pb.ChildTarget
   244  		exists = true
   245  		return nil
   246  	})
   247  	return target, exists
   248  }
   249  
   250  // Close safely closes the block database.
   251  func (cs *ConsensusSet) Close() error {
   252  	return cs.tg.Stop()
   253  }
   254  
   255  // managedCurrentBlock returns the latest block in the heaviest known blockchain.
   256  func (cs *ConsensusSet) managedCurrentBlock() (block types.Block) {
   257  	cs.mu.RLock()
   258  	defer cs.mu.RUnlock()
   259  
   260  	_ = cs.db.View(func(tx *bolt.Tx) error {
   261  		pb := currentProcessedBlock(tx)
   262  		block = pb.Block
   263  		return nil
   264  	})
   265  	return block
   266  }
   267  
   268  // CurrentBlock returns the latest block in the heaviest known blockchain.
   269  func (cs *ConsensusSet) CurrentBlock() (block types.Block) {
   270  	// A call to a closed database can cause undefined behavior.
   271  	err := cs.tg.Add()
   272  	if err != nil {
   273  		return types.Block{}
   274  	}
   275  	defer cs.tg.Done()
   276  
   277  	// Block until a lock can be grabbed on the consensus set, indicating that
   278  	// all modules have received the most recent block. The lock is held so that
   279  	// there are no race conditions when trying to synchronize nodes.
   280  	cs.mu.Lock()
   281  	defer cs.mu.Unlock()
   282  
   283  	_ = cs.db.View(func(tx *bolt.Tx) error {
   284  		pb := currentProcessedBlock(tx)
   285  		block = pb.Block
   286  		return nil
   287  	})
   288  	return block
   289  }
   290  
   291  // Flush will block until the consensus set has finished all in-progress
   292  // routines.
   293  func (cs *ConsensusSet) Flush() error {
   294  	return cs.tg.Flush()
   295  }
   296  
   297  // Height returns the height of the consensus set.
   298  func (cs *ConsensusSet) Height() (height types.BlockHeight) {
   299  	// A call to a closed database can cause undefined behavior.
   300  	err := cs.tg.Add()
   301  	if err != nil {
   302  		return 0
   303  	}
   304  	defer cs.tg.Done()
   305  
   306  	// Block until a lock can be grabbed on the consensus set, indicating that
   307  	// all modules have received the most recent block. The lock is held so that
   308  	// there are no race conditions when trying to synchronize nodes.
   309  	cs.mu.Lock()
   310  	defer cs.mu.Unlock()
   311  
   312  	_ = cs.db.View(func(tx *bolt.Tx) error {
   313  		height = blockHeight(tx)
   314  		return nil
   315  	})
   316  	return height
   317  }
   318  
   319  // InCurrentPath returns true if the block presented is in the current path,
   320  // false otherwise.
   321  func (cs *ConsensusSet) InCurrentPath(id types.BlockID) (inPath bool) {
   322  	// A call to a closed database can cause undefined behavior.
   323  	err := cs.tg.Add()
   324  	if err != nil {
   325  		return false
   326  	}
   327  	defer cs.tg.Done()
   328  
   329  	_ = cs.db.View(func(tx *bolt.Tx) error {
   330  		pb, err := getBlockMap(tx, id)
   331  		if err != nil {
   332  			inPath = false
   333  			return nil
   334  		}
   335  		pathID, err := getPath(tx, pb.Height)
   336  		if err != nil {
   337  			inPath = false
   338  			return nil
   339  		}
   340  		inPath = pathID == id
   341  		return nil
   342  	})
   343  	return inPath
   344  }
   345  
   346  // MinimumValidChildTimestamp returns the earliest timestamp that the next block
   347  // can have in order for it to be considered valid.
   348  func (cs *ConsensusSet) MinimumValidChildTimestamp(id types.BlockID) (timestamp types.Timestamp, exists bool) {
   349  	// A call to a closed database can cause undefined behavior.
   350  	err := cs.tg.Add()
   351  	if err != nil {
   352  		return 0, false
   353  	}
   354  	defer cs.tg.Done()
   355  
   356  	// Error is not checked because it does not matter.
   357  	_ = cs.db.View(func(tx *bolt.Tx) error {
   358  		pb, err := getBlockMap(tx, id)
   359  		if err != nil {
   360  			return err
   361  		}
   362  		timestamp = cs.blockRuleHelper.minimumValidChildTimestamp(tx.Bucket(BlockMap), pb)
   363  		exists = true
   364  		return nil
   365  	})
   366  	return timestamp, exists
   367  }
   368  
   369  // StorageProofSegment returns the segment to be used in the storage proof for
   370  // a given file contract.
   371  func (cs *ConsensusSet) StorageProofSegment(fcid types.FileContractID) (index uint64, err error) {
   372  	// A call to a closed database can cause undefined behavior.
   373  	err = cs.tg.Add()
   374  	if err != nil {
   375  		return 0, err
   376  	}
   377  	defer cs.tg.Done()
   378  
   379  	_ = cs.db.View(func(tx *bolt.Tx) error {
   380  		index, err = storageProofSegment(tx, fcid)
   381  		return nil
   382  	})
   383  	return index, err
   384  }