ouroboros-consensus-0.18.0.0: Consensus layer for the Ouroboros blockchain protocol
Safe HaskellSafe-Inferred
LanguageHaskell2010

Ouroboros.Consensus.Storage.ChainDB.Impl.ChainSel

Description

Operations involving chain selection: the initial chain selection and adding a block.

Synopsis

Documentation

addBlockAsync ∷ ∀ m blk. (IOLike m, HasHeader blk) ⇒ ChainDbEnv m blk → InvalidBlockPunishment m → blk → m (AddBlockPromise m blk) Source #

Add a block to the ChainDB, asynchronously.

This adds a BlockToAdd corresponding to the given block to the cdbChainSelQueue queue. The entries in that queue are processed using chainSelSync, see that function for more information.

When the queue is full, this function will still block.

An important advantage of this asynchronous approach over a synchronous approach is that it doesn't have the following disadvantage: when a thread adding a block to the ChainDB is killed, which can happen when disconnecting from the corresponding node, we might have written the block to disk, but not updated the corresponding in-memory state (e.g., that of the VolatileDB), leaving both out of sync.

With this asynchronous approach, threads adding blocks asynchronously can be killed without worries, the background thread processing the blocks synchronously won't be killed. Only when the whole ChainDB shuts down will that background thread get killed. But since there will be no more in-memory state, it can't get out of sync with the file system state. On the next startup, a correct in-memory state will be reconstructed from the file system state.

chainSelSync ∷ ∀ m blk. (IOLike m, LedgerSupportsProtocol blk, BlockSupportsDiffusionPipelining blk, InspectLedger blk, HasHardForkHistory blk, HasCallStack) ⇒ ChainDbEnv m blk → ChainSelMessage m blk → Electric m () Source #

Add a block to the ChainDB, synchronously.

This is the only operation that actually changes the ChainDB. It will store the block on disk and trigger chain selection, possibly switching to a fork.

When the slot of the block is > the current slot, a chain selection will be scheduled in the slot of the block.

chainSelectionForBlock ∷ ∀ m blk. (IOLike m, LedgerSupportsProtocol blk, BlockSupportsDiffusionPipelining blk, InspectLedger blk, HasHardForkHistory blk, HasCallStack) ⇒ ChainDbEnv m blk → BlockCache blk → Header blk → InvalidBlockPunishment m → Electric m (Point blk) Source #

Trigger chain selection for the given block.

PRECONDITION: the block is in the VolatileDB.

PRECONDITION: the slot of the block <= the current (wall) slot

The new tip of the current chain is returned.

Constructing candidate fragments

The VolatileDB keeps a "successors" map in memory, telling us the hashes of the known successors of any block, but it does not keep headers in memory, which are needed to construct candidate fargments. We try to reuse the headers from the current chain fragment where possible, but it will not contain all needed headers. This means that we will need to read some blocks from disk and extract their headers. Under normal circumstances this does not matter too much; although this will be done every time we add a block, the expected number of headers to read from disk is very small:

  • None if we stay on the current chain and this is just the next block
  • A handful if we stay on the current chain and the block we just received was a missing block and we already received some of its successors
  • A handful if we switch to a short fork

This is expensive only

  • on startup: in this case we need to read at least k blocks from the VolatileDB, and possibly more if there are some other chains in the VolatileDB starting from the tip of the ImmutableDB
  • when we switch to a distant fork

This cost is currently deemed acceptable.

initialChainSelection ∷ ∀ m blk. (IOLike m, LedgerSupportsProtocol blk, BlockSupportsDiffusionPipelining blk) ⇒ ImmutableDB m blk → VolatileDB m blk → LgrDB m blk → Tracer m (TraceInitChainSelEvent blk) → TopLevelConfig blk → StrictTVar m (WithFingerprint (InvalidBlocks blk)) → StrictTVar m (FutureBlocks m blk) → CheckInFuture m blk → LoE (m (AnchoredFragment (Header blk))) → m (ChainAndLedger blk) Source #

Perform the initial chain selection based on the tip of the ImmutableDB and the contents of the VolatileDB.

Returns the chosen validated chain and corresponding ledger.

See "## Initialization" in ChainDB.md.

triggerChainSelectionAsync ∷ ∀ m blk. IOLike m ⇒ ChainDbEnv m blk → m () Source #

Schedule reprocessing of blocks postponed by the LoE.

Exported for testing purposes

olderThanK Source #

Arguments

HasHeader (Header blk) 
Header blk

Header of the block to add

IsEBB

Whether the block is an EBB or not

WithOrigin BlockNo

The block number of the most recent "immutable" block, i.e., the block k blocks back.

Bool 

Return True when the given header should be ignored when adding it because it is too old, i.e., we wouldn't be able to switch to a chain containing the corresponding block because its block number is more than k blocks or exactly k blocks back.

Special case: the header corresponds to an EBB which has the same block number as the block k blocks back (the most recent "immutable" block). As EBBs share their block number with the block before them, the EBB is not too old in that case and can be adopted as part of our chain.

This special case can occur, for example, when the VolatileDB is empty (because of corruption). The "immutable" block is then also the tip of the chain. If we then try to add the EBB after it, it will have the same block number, so we must allow it.