Safe Haskell | None |
---|---|
Language | Haskell2010 |
Ouroboros.Consensus.Storage.ChainDB.Impl.ChainSel
Contents
Description
Operations involving chain selection: the initial chain selection and adding a block.
Synopsis
- addBlockAsync :: forall m blk. ( IOLike m, HasHeader blk) => ChainDbEnv m blk -> InvalidBlockPunishment m -> blk -> m ( AddBlockPromise m blk)
- addBlockSync :: forall m blk. ( IOLike m, GetPrevHash blk, LedgerSupportsProtocol blk, InspectLedger blk, HasHardForkHistory blk, HasCallStack ) => ChainDbEnv m blk -> BlockToAdd m blk -> m ()
- chainSelectionForBlock :: forall m blk. ( IOLike m, HasHeader blk, LedgerSupportsProtocol blk, InspectLedger blk, HasHardForkHistory blk, HasCallStack ) => ChainDbEnv m blk -> BlockCache blk -> Header blk -> InvalidBlockPunishment m -> m ( Point blk)
- initialChainSelection :: forall m blk. ( IOLike m, LedgerSupportsProtocol 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 -> m (ChainAndLedger blk)
- olderThanK :: HasHeader ( Header blk) => Header blk -> IsEBB -> WithOrigin BlockNo -> Bool
Documentation
addBlockAsync :: forall 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
cdbBlocksToAdd
queue. The entries in that queue are processed using
addBlockSync
, 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.
addBlockSync :: forall m blk. ( IOLike m, GetPrevHash blk, LedgerSupportsProtocol blk, InspectLedger blk, HasHardForkHistory blk, HasCallStack ) => ChainDbEnv m blk -> BlockToAdd m blk -> 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 :: forall m blk. ( IOLike m, HasHeader blk, LedgerSupportsProtocol blk, InspectLedger blk, HasHardForkHistory blk, HasCallStack ) => ChainDbEnv m blk -> BlockCache blk -> Header blk -> InvalidBlockPunishment m -> 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 :: forall m blk. ( IOLike m, LedgerSupportsProtocol 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 -> 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.
Exported for testing purposes
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
|
-> 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.