cardano-wallet-core-2022.7.1: The Wallet Backend for a Cardano node.
Copyright © 2021 IOHK
License Apache-2.0
Safe Haskell None
Language Haskell2010

Cardano.Wallet.Checkpoints

Description

Data type that represents a collection of checkpoints. Each checkpoints is associated with a Slot .

Synopsis

Checkpoints

checkpoints :: Checkpoints a -> Map Slot a Source #

Map of checkpoints. Always contains the genesis checkpoint.

loadCheckpoints :: [( Slot , a)] -> Checkpoints a Source #

Turn the list of checkpoints into a map of checkpoints.

FIXME LATER during ADP-1043: The database actually does not store the checkpoint at genesis, but the checkpoint after that. Hence, this function does not check whether the genesis checkpoint is in the list of checkpoints.

fromGenesis :: a -> Checkpoints a Source #

Begin with the genesis checkpoint.

getLatest :: Checkpoints a -> ( Slot , a) Source #

Get the checkpoint with the largest SlotNo .

findNearestPoint :: Checkpoints a -> Slot -> Maybe Slot Source #

Find the nearest Checkpoint that is either at the given point or before.

Delta types

data DeltaCheckpoints a Source #

Constructors

PutCheckpoint Slot a
RollbackTo Slot
RestrictTo [ Slot ]

Restrict to the intersection of this list with the checkpoints that are already present. The genesis checkpoint will always be present.

Checkpoint hygiene

data SparseCheckpointsConfig Source #

Captures the configuration for the sparseCheckpoints function.

NOTE: large values of edgeSize aren't recommended as they would mean storing many unnecessary checkpoints. In Ouroboros Praos, there's a reasonable probability for small forks each a few blocks deep so it makes sense to maintain a small part that is denser near the edge.

sparseCheckpoints Source #

Arguments

:: SparseCheckpointsConfig

Parameters for the function.

-> Quantity "block" Word32

A given block height

-> [ Word32 ]

The list of checkpoint heights that should be kept in DB.

Storing EVERY checkpoints in the database is quite expensive and useless. We make the following assumptions:

  • We can't rollback for more than `k=epochStability` blocks in the past
  • It is pretty fast to re-sync a few hundred blocks
  • Small rollbacks may occur more often than deep ones

So, as we insert checkpoints, we make sure to:

  • Prune any checkpoint that more than k blocks in the past
  • Keep only one checkpoint every 100 blocks
  • But still keep ~10 most recent checkpoints to cope with small rollbacks

Example 1 : Inserting cp153

ℹ: cp142 is discarded and cp153 inserted.

 Currently in DB:
┌───┬───┬───┬─  ──┬───┐
│cp000 │cp100 │cp142 │..    ..│cp152 │
└───┴───┴───┴─  ──┴───┘
 Want in DB:
┌───┬───┬───┬─  ──┬───┐
│cp000 │cp100 │cp143 │..    ..│cp153 │
└───┴───┴───┴─  ──┴───┘
 

Example 2 : Inserting cp111

ℹ: cp100 is kept and cp111 inserted.

 Currently in DB:
┌───┬───┬───┬─  ──┬───┐
│cp000 │cp100 │cp101 │..    ..│cp110 │
└───┴───┴───┴─  ──┴───┘
 Want in DB:
┌───┬───┬───┬─  ──┬───┐
│cp000 │cp100 │cp101 │..    ..│cp111 │
└───┴───┴───┴─  ──┴───┘
 

NOTE: There might be cases where the chain following "fails" (because, for example, the node has switched to a different chain, different by more than k), and in such cases, we have no choice but rolling back from genesis. Therefore, we need to keep the very first checkpoint in the database, no matter what.

gapSize :: SparseCheckpointsConfig -> Word32 Source #

A reasonable gap size used internally in sparseCheckpoints .

Reasonable means that it's not _too frequent_ and it's not too large. A value that is too small in front of k would require generating much more checkpoints than necessary.

A value that is larger than k may have dramatic consequences in case of deep rollbacks.

As a middle ground, we current choose `k / 3`, which is justified by:

  • The current speed of the network layer (several thousands blocks per seconds)
  • The current value of k = 2160

So, `k / 3` = 720, which should remain around a second of time needed to catch up in case of large rollbacks.