gitlab.com/jokerrs1/Sia@v1.3.2/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  	"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  	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  // ChildTarget returns the target for the child of a block.
   206  func (cs *ConsensusSet) ChildTarget(id types.BlockID) (target types.Target, exists bool) {
   207  	// A call to a closed database can cause undefined behavior.
   208  	err := cs.tg.Add()
   209  	if err != nil {
   210  		return types.Target{}, false
   211  	}
   212  	defer cs.tg.Done()
   213  
   214  	_ = cs.db.View(func(tx *bolt.Tx) error {
   215  		pb, err := getBlockMap(tx, id)
   216  		if err != nil {
   217  			return err
   218  		}
   219  		target = pb.ChildTarget
   220  		exists = true
   221  		return nil
   222  	})
   223  	return target, exists
   224  }
   225  
   226  // Close safely closes the block database.
   227  func (cs *ConsensusSet) Close() error {
   228  	return cs.tg.Stop()
   229  }
   230  
   231  // managedCurrentBlock returns the latest block in the heaviest known blockchain.
   232  func (cs *ConsensusSet) managedCurrentBlock() (block types.Block) {
   233  	cs.mu.RLock()
   234  	defer cs.mu.RUnlock()
   235  
   236  	_ = cs.db.View(func(tx *bolt.Tx) error {
   237  		pb := currentProcessedBlock(tx)
   238  		block = pb.Block
   239  		return nil
   240  	})
   241  	return block
   242  }
   243  
   244  // CurrentBlock returns the latest block in the heaviest known blockchain.
   245  func (cs *ConsensusSet) CurrentBlock() (block types.Block) {
   246  	// A call to a closed database can cause undefined behavior.
   247  	err := cs.tg.Add()
   248  	if err != nil {
   249  		return types.Block{}
   250  	}
   251  	defer cs.tg.Done()
   252  
   253  	// Block until a lock can be grabbed on the consensus set, indicating that
   254  	// all modules have received the most recent block. The lock is held so that
   255  	// there are no race conditions when trying to synchronize nodes.
   256  	cs.mu.Lock()
   257  	defer cs.mu.Unlock()
   258  
   259  	_ = cs.db.View(func(tx *bolt.Tx) error {
   260  		pb := currentProcessedBlock(tx)
   261  		block = pb.Block
   262  		return nil
   263  	})
   264  	return block
   265  }
   266  
   267  // Flush will block until the consensus set has finished all in-progress
   268  // routines.
   269  func (cs *ConsensusSet) Flush() error {
   270  	return cs.tg.Flush()
   271  }
   272  
   273  // Height returns the height of the consensus set.
   274  func (cs *ConsensusSet) Height() (height types.BlockHeight) {
   275  	// A call to a closed database can cause undefined behavior.
   276  	err := cs.tg.Add()
   277  	if err != nil {
   278  		return 0
   279  	}
   280  	defer cs.tg.Done()
   281  
   282  	// Block until a lock can be grabbed on the consensus set, indicating that
   283  	// all modules have received the most recent block. The lock is held so that
   284  	// there are no race conditions when trying to synchronize nodes.
   285  	cs.mu.Lock()
   286  	defer cs.mu.Unlock()
   287  
   288  	_ = cs.db.View(func(tx *bolt.Tx) error {
   289  		height = blockHeight(tx)
   290  		return nil
   291  	})
   292  	return height
   293  }
   294  
   295  // InCurrentPath returns true if the block presented is in the current path,
   296  // false otherwise.
   297  func (cs *ConsensusSet) InCurrentPath(id types.BlockID) (inPath bool) {
   298  	// A call to a closed database can cause undefined behavior.
   299  	err := cs.tg.Add()
   300  	if err != nil {
   301  		return false
   302  	}
   303  	defer cs.tg.Done()
   304  
   305  	_ = cs.db.View(func(tx *bolt.Tx) error {
   306  		pb, err := getBlockMap(tx, id)
   307  		if err != nil {
   308  			inPath = false
   309  			return nil
   310  		}
   311  		pathID, err := getPath(tx, pb.Height)
   312  		if err != nil {
   313  			inPath = false
   314  			return nil
   315  		}
   316  		inPath = pathID == id
   317  		return nil
   318  	})
   319  	return inPath
   320  }
   321  
   322  // MinimumValidChildTimestamp returns the earliest timestamp that the next block
   323  // can have in order for it to be considered valid.
   324  func (cs *ConsensusSet) MinimumValidChildTimestamp(id types.BlockID) (timestamp types.Timestamp, exists bool) {
   325  	// A call to a closed database can cause undefined behavior.
   326  	err := cs.tg.Add()
   327  	if err != nil {
   328  		return 0, false
   329  	}
   330  	defer cs.tg.Done()
   331  
   332  	// Error is not checked because it does not matter.
   333  	_ = cs.db.View(func(tx *bolt.Tx) error {
   334  		pb, err := getBlockMap(tx, id)
   335  		if err != nil {
   336  			return err
   337  		}
   338  		timestamp = cs.blockRuleHelper.minimumValidChildTimestamp(tx.Bucket(BlockMap), pb)
   339  		exists = true
   340  		return nil
   341  	})
   342  	return timestamp, exists
   343  }
   344  
   345  // StorageProofSegment returns the segment to be used in the storage proof for
   346  // a given file contract.
   347  func (cs *ConsensusSet) StorageProofSegment(fcid types.FileContractID) (index uint64, err error) {
   348  	// A call to a closed database can cause undefined behavior.
   349  	err = cs.tg.Add()
   350  	if err != nil {
   351  		return 0, err
   352  	}
   353  	defer cs.tg.Done()
   354  
   355  	_ = cs.db.View(func(tx *bolt.Tx) error {
   356  		index, err = storageProofSegment(tx, fcid)
   357  		return nil
   358  	})
   359  	return index, err
   360  }