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

Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

Description

ChainSync jumping (CSJ) is an optimization for the ChainSync protocol that allows nodes to sync without downloading headers from all of the honest peers. This load is undesirable as it slows down all the peers involved.

The idea is to download the headers of a chain from a single peer (the dynamo) and then ask periodically to the other peers (the jumpers) whether they agree with the dynamo's chain.

When the jumpers disagree with the dynamo, the jumper with the oldest intersection is asked to compete with the dynamo in the GDD logic (becoming an objector). If the dynamo is disconnected, a new dynamo is elected and the objector is demoted to a jumper.

If the objector is disconnected, the syncing process continues with the dynamo and the remaining jumpers.

The main property of the algorithm is that it never downloads headers from more than two plausibly honest peers at a time (a dynamo and an objector). All other peers are either waiting their turn to compete with the dynamo, or are in agreement with it, or are disengaged (see next section).

The algorithm might still download headers redundantly from peers that do historical rollbacks. These rollbacks, however, constitute dishonest behavior, and CSJ does not concern itself with avoiding load to dishonest peers. Avoiding the load induced by dishonest peers on the syncing node would require additionally to disconnect peers that do historical rollbacks. This is not done by CSJ.

Interactions with the Genesis Density Disconnection logic ---------------------------------------------------------

It is possible that neither the dynamo nor the objector are disconnected. This could happen if: 1. They both serve the same chain, or 2. They both claim to have no more headers.

To avoid (1) CSJ checks that the objector disagrees with the dynamo at the point it claimed to disagree as a jumper. If the objector agrees with the dynamo, it is disengaged. A disengaged peer is not asked to jump or act as dynamo or objector. Instead, it continues to offer headers for the rest of the syncing. When the objector is disengaged, a new objector is elected among the dissenting jumpers. If there are no dissenting jumpers left, the syncing continues with the dynamo and the remaining jumpers.

To prevent the dynamo from agreeing with the objector instead, the dynamo is not allowed to rollback before the last jump it requested. If the dynamo tries to rollback before the last jump, it is disengaged and a new dynamo is elected.

To avoid (2) CSJ disengages a peer as soon as it claims to have no more headers. Syncing continues with a new elected dynamo or objector depending on the disengaged peer's role.

CSJ finishes and is turned off when all peers have been disengaged.

Interactions with the ChainSync client --------------------------------------

The ChainSync client interacts with CSJ through some callbacks that determine when the client should pause, download headers, or ask about agreement with a given point (jumping). See the Jumping type for more details.

Interactions with the Limit on Patience ---------------------------------------

Jumpers don't leak the Limit on Patience (LoP) bucket until they are promoted to dynamos or objectors. And the leaking is stopped as soon as they are demoted.

If a jumper refrains from answering to jumps, they will be disconnected with the intersectTimeout (in ChainSyncTimeout).

A jumper answering just before the timeout will not delay the syncing process by a large amount. If they agree with the dynamo, the dynamo will be busy downloading headers and validating blocks while the jumper answers. If the jumper disagrees with the dynamo, CSJ will look for the precise intersection with the dynamo's chain. This could take a few minutes, but it is a path that will end up in one of the dynamo and the jumper being disconnected or disengaged.

Overview of the state transitions ---------------------------------

See ChainSyncJumpingState for the implementation of the states.

               j       ╔════════╗
           ╭────────── ║ Dynamo ║ ◀─────────╮
           │           ╚════════╝           │f
           ▼                  ▲             │
   ┌────────────┐             │     k     ┌──────────┐
   │ Disengaged │ ◀───────────│────────── │ Objector │
   └────────────┘       ╭─────│────────── └──────────┘
                        │     │             ▲    ▲ │
                       g│     │e         b  │    │ │
                        │     │       ╭─────╯   i│ │c
                ╭╌╌╌╌╌╌╌▼╌╌╌╌╌╌╌╌╌╌╌╌╌│╌╌╌╌╌╌╌╌╌╌│╌▼╌╌╌╮
                ┆ ╔═══════╗  a   ┌──────┐  d   ┌─────┐ |
                ┆ ║ Happy ║ ───▶ │ LFI* │ ───▶ │ FI* │ |
                ┆ ╚═══════╝ ◀─╮  └──────┘      └─────┘ |
                ┆ Jumper      ╰─────┴────────────╯h    |
                ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
  • : LookingForIntersection and FoundIntersection, abbreviated for this drawing only; this abbreviation will not be used elsewhere.

A new peer starts as the dynamo if there is no other peer or as a Happy jumper otherwise. The dynamo periodically requests jumps from happy jumpers who, in the ideal case, accept them.

In the event that a jumper rejects a jump, it goes from Happy to LFI* (a). From there starts a back-and-forth of intersection search messages until the exact point of disagreement with the dynamo is found.

Once the exact point of disagreement is found, and if there is no objector yet, the jumper becomes the objector (b). If there is an objector, then we compare the intersections of the objector and the jumper. If the jumper's intersection is strictly older, then the jumper replaces the objector (b+c). Otherwise, the jumper is marked as FI* (d).

If the dynamo disconnects or is disengaged, one peer is elected as the new dynamo (e|f) and all other peers revert to being happy jumpers (g+h).

If the objector disconnects or is disengaged, and there are FI* jumpers, then the one with the oldest intersection with the dynamo gets elected (i).

If the dynamo rolls back to a point older than the last jump it requested, it is disengaged (j) and a new dynamo is elected (e|f).

If the objector agrees with the dynamo, it is disengaged (k). If there are FI* jumpers, then one of them gets elected as the new objector (i).

If dynamo or objector claim to have no more headers, they are disengaged (j|k).

Synopsis

Documentation

type Context = ContextWith () () Source #

A non-specific, generic context for ChainSync jumping.

data ContextWith peerField handleField m peer blk Source #

A context for ChainSync jumping

Invariants:

  • If handlesVar is not empty, then there is exactly one dynamo in it.
  • There is at most one objector in handlesVar.
  • If there exist FoundIntersection jumpers in handlesVar, then there is an objector and the intersection of the objector with the dynamo is at least as old as the oldest intersection of the FoundIntersection jumpers with the dynamo.

Constructors

Context 

Fields

data Instruction blk Source #

Instruction from the jumping governor, either to run normal ChainSync, or to jump to follow a dynamo with the given fragment, or to restart ChainSync.

Constructors

RunNormally 
Restart

The restart instruction restarts the ChainSync protocol. This is necessary when disengaging a peer of which we know no point that we could set the intersection of the ChainSync server to.

JumpInstruction !(JumpInstruction blk)

Jump to the tip of the given fragment.

Instances

Instances details
Generic (Instruction blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

Associated Types

type Rep (Instruction blk) ∷ TypeType #

Methods

fromInstruction blk → Rep (Instruction blk) x #

toRep (Instruction blk) x → Instruction blk #

(HasHeader (Header blk), Show (Header blk)) ⇒ Show (Instruction blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

Methods

showsPrecIntInstruction blk → ShowS #

showInstruction blk → String #

showList ∷ [Instruction blk] → ShowS #

(HasHeader (Header blk), Eq (Header blk)) ⇒ Eq (Instruction blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

Methods

(==)Instruction blk → Instruction blk → Bool #

(/=)Instruction blk → Instruction blk → Bool #

(HasHeader blk, LedgerSupportsProtocol blk, NoThunks (Header blk)) ⇒ NoThunks (Instruction blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

type Rep (Instruction blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

type Rep (Instruction blk) = D1 ('MetaData "Instruction" "Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping" "ouroboros-consensus-0.21.0.0-inplace" 'False) (C1 ('MetaCons "RunNormally" 'PrefixI 'False) (U1TypeType) :+: (C1 ('MetaCons "Restart" 'PrefixI 'False) (U1TypeType) :+: C1 ('MetaCons "JumpInstruction" 'PrefixI 'False) (S1 ('MetaSel ('NothingMaybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (JumpInstruction blk)))))

data JumpInstruction blk Source #

Constructors

JumpTo !(JumpInfo blk) 
JumpToGoodPoint !(JumpInfo blk)

Used to set the intersection of the ChainSync servers of starting objectors and dynamos. Otherwise, the ChainSync server wouldn't know which headers to start serving.

Instances

Instances details
Generic (JumpInstruction blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

Associated Types

type Rep (JumpInstruction blk) ∷ TypeType #

Methods

fromJumpInstruction blk → Rep (JumpInstruction blk) x #

toRep (JumpInstruction blk) x → JumpInstruction blk #

(HasHeader (Header blk), Show (Header blk)) ⇒ Show (JumpInstruction blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

Methods

showsPrecIntJumpInstruction blk → ShowS #

showJumpInstruction blk → String #

showList ∷ [JumpInstruction blk] → ShowS #

(HasHeader (Header blk), Eq (Header blk)) ⇒ Eq (JumpInstruction blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

Methods

(==)JumpInstruction blk → JumpInstruction blk → Bool #

(/=)JumpInstruction blk → JumpInstruction blk → Bool #

(HasHeader blk, LedgerSupportsProtocol blk, NoThunks (Header blk)) ⇒ NoThunks (JumpInstruction blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

type Rep (JumpInstruction blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

type Rep (JumpInstruction blk) = D1 ('MetaData "JumpInstruction" "Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping" "ouroboros-consensus-0.21.0.0-inplace" 'False) (C1 ('MetaCons "JumpTo" 'PrefixI 'False) (S1 ('MetaSel ('NothingMaybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (JumpInfo blk))) :+: C1 ('MetaCons "JumpToGoodPoint" 'PrefixI 'False) (S1 ('MetaSel ('NothingMaybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (JumpInfo blk))))

data JumpResult blk Source #

The result of a jump request, either accepted or rejected.

Instances

Instances details
Generic (JumpResult blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

Associated Types

type Rep (JumpResult blk) ∷ TypeType #

Methods

fromJumpResult blk → Rep (JumpResult blk) x #

toRep (JumpResult blk) x → JumpResult blk #

(HasHeader (Header blk), Show (Header blk)) ⇒ Show (JumpResult blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

Methods

showsPrecIntJumpResult blk → ShowS #

showJumpResult blk → String #

showList ∷ [JumpResult blk] → ShowS #

(HasHeader (Header blk), Eq (Header blk)) ⇒ Eq (JumpResult blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

Methods

(==)JumpResult blk → JumpResult blk → Bool #

(/=)JumpResult blk → JumpResult blk → Bool #

(HasHeader blk, LedgerSupportsProtocol blk, NoThunks (Header blk)) ⇒ NoThunks (JumpResult blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

type Rep (JumpResult blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

type Rep (JumpResult blk) = D1 ('MetaData "JumpResult" "Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping" "ouroboros-consensus-0.21.0.0-inplace" 'False) (C1 ('MetaCons "AcceptedJump" 'PrefixI 'False) (S1 ('MetaSel ('NothingMaybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (JumpInstruction blk))) :+: C1 ('MetaCons "RejectedJump" 'PrefixI 'False) (S1 ('MetaSel ('NothingMaybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (JumpInstruction blk))))

data Jumping m blk Source #

Hooks for ChainSync jumping.

Constructors

Jumping 

Fields

  • jgNextInstruction ∷ !(m (Instruction blk))

    Get the next instruction to execute, which can be either to run normal ChainSync, to jump to a given point, or to restart ChainSync. When the peer is a jumper and there is no jump request, jgNextInstruction blocks until a jump request is made.

  • jgOnAwaitReply ∷ !(m ())

    To be called whenever the peer claims to have no more headers.

  • jgOnRollForward ∷ !(Point (Header blk) → m ())

    To be called whenever a header is received from the peer before it is validated.

  • jgOnRollBackward ∷ !(WithOrigin SlotNo → m ())

    To be called whenever a peer rolls back.

  • jgProcessJumpResult ∷ !(JumpResult blk → m ())

    Process the result of a jump, either accepted or rejected.

    The jump result is used to decide on the next jumps or whether to elect an objector.

  • jgUpdateJumpInfo ∷ !(JumpInfo blk → STM m ())

    To be called to update the last known jump possible to the tip of the peers candidate fragment. The ChainSync clients for all peers should call this function in case they are or they become dynamos.

    JumpInfo is meant to be a snapshot of the KnownIntersectionState of the ChainSync client. See JumpInfo for more details.

Instances

Instances details
Generic (Jumping m blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

Associated Types

type Rep (Jumping m blk) ∷ TypeType #

Methods

fromJumping m blk → Rep (Jumping m blk) x #

toRep (Jumping m blk) x → Jumping m blk #

(IOLike m, HasHeader blk, NoThunks (Header blk)) ⇒ NoThunks (Jumping m blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

type Rep (Jumping m blk) Source # 
Instance details

Defined in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping

type Rep (Jumping m blk) = D1 ('MetaData "Jumping" "Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping" "ouroboros-consensus-0.21.0.0-inplace" 'False) (C1 ('MetaCons "Jumping" 'PrefixI 'True) ((S1 ('MetaSel ('Just "jgNextInstruction") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (m (Instruction blk))) :*: (S1 ('MetaSel ('Just "jgOnAwaitReply") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (m ())) :*: S1 ('MetaSel ('Just "jgOnRollForward") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Point (Header blk) → m ())))) :*: (S1 ('MetaSel ('Just "jgOnRollBackward") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (WithOrigin SlotNo → m ())) :*: (S1 ('MetaSel ('Just "jgProcessJumpResult") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (JumpResult blk → m ())) :*: S1 ('MetaSel ('Just "jgUpdateJumpInfo") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (JumpInfo blk → STM m ()))))))

makeContext Source #

Arguments

MonadSTM m 
StrictTVar m (Map peer (ChainSyncClientHandle m blk)) 
SlotNo

The size of jumps, in number of slots.

STM m (Context m peer blk) 

mkJumping ∷ (MonadSTM m, Eq peer, LedgerSupportsProtocol blk) ⇒ PeerContext m peer blk → Jumping m blk Source #

Create the callbacks for a given peer.

noJumpingMonadSTM m ⇒ Jumping m blk Source #

No-op implementation of CSJ

registerClient Source #

Arguments

∷ (Ord peer, LedgerSupportsProtocol blk, IOLike m) 
Context m peer blk 
→ peer 
StrictTVar m (ChainSyncState blk) 
→ (StrictTVar m (ChainSyncJumpingState m blk) → ChainSyncClientHandle m blk)

A function to make a client handle from a jumping state.

STM m (PeerContext m peer blk) 

Register a new ChainSync client to a context, returning a PeerContext for that peer. If there is no dynamo, the peer starts as dynamo; otherwise, it starts as a jumper.

unregisterClient ∷ (MonadSTM m, Ord peer, LedgerSupportsProtocol blk) ⇒ PeerContext m peer blk → STM m () Source #

Unregister a client from a PeerContext; this might trigger the election of a new dynamo or objector if the peer was one of these two.