ouroboros-consensus-0.27.0.0: Consensus layer for the Ouroboros blockchain protocol
Safe HaskellNone
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.

CSJ depends on the ChainSync client to disconnect dynamos that have an empty genesis window after their intersection with the selection. This is necessary because otherwise there are no points to jump to, and CSJ could would get stuck when the dynamo blocks on the forecast horizon. See Note [Candidate comparing beyond the forecast horizon] in Ouroboros.Consensus.MiniProtocol.ChainSync.Client.

Interactions with the BlockFetch logic --------------------------------------

When syncing, the BlockFetch logic might request to change the dynamo with a call to rotateDynamo. This is because the choice of dynamo influences which peer is selected to download blocks. See the note "Interactions with ChainSync Jumping" in Ouroboros.Network.BlockFetch.Decision.BulkSync.

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 │
   └────────────┘   │   ╭─────│────────── └──────────┘
                    │   │     │             ▲    ▲ │
                   l│  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.

In the following walk-through, we will point to transitions in the drawing between parentheses, like so: (a) (b+c) (e|f). We will use + to express that both transitions happen simultaneously (for different peers) and `|` to express a choice.

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 and remain happy jumpers.

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 intersection of the objector with the dynamo and the intersection of the jumper with the dynamo. If the jumper's intersection is strictly older, then the jumper replaces the objector, who is marked as FI* (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 the 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). Otherwise, we are left with no objector.

If the dynamo rolls back to a point older than the last jump it requested, it is disengaged (j), a new dynamo is elected (e|f), and all the other peers revert to being happy jumpers (g+h).

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). Otherwise, we are left with no objector.

If the dynamo or the objector claim to have no more headers, they are disengaged (j|k), triggering the same chain of effect as described in the two previous points.

The BlockFetch logic can ask to change the dynamo if it is not serving blocks fast enough. If there are other non-disengaged peers, all peers are demoted to happy jumpers (l+g+h) and a new dynamo is elected (e).

Synopsis

Documentation

type Context = ContextWith () () Source #

A non-specific, generic context for ChainSync jumping.

data ContextWith peerField handleField (m ∷ TypeType) peer blk Source #

A context for ChainSync jumping

Invariants:

  • If handlesCol is not empty, then there is exactly one dynamo in it.
  • There is at most one objector in handlesCol.
  • If there exist FoundIntersection jumpers in handlesCol, 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) 
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.27.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)))))

Methods

fromInstruction blk → Rep (Instruction blk) x #

toRep (Instruction blk) x → Instruction blk #

(Typeable 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 #

(Typeable blk, 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.27.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) 
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.27.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))))

Methods

fromJumpInstruction blk → Rep (JumpInstruction blk) x #

toRep (JumpInstruction blk) x → JumpInstruction blk #

(Typeable 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 #

(Typeable blk, 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.27.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) 
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.27.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))))

Methods

fromJumpResult blk → Rep (JumpResult blk) x #

toRep (JumpResult blk) x → JumpResult blk #

(Typeable 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 #

(Typeable blk, 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.27.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 ∷ TypeType) 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) 
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.27.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 ()))))))

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.27.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 ()))))))

data TraceEventCsj peer blk Source #

Events arising from a specific ChainSync client

Constructors

BecomingObjector (Maybe peer)

previous objector

BlockedOnJump 
InitializedAsDynamo 
NoLongerDynamo (Maybe peer) TraceCsjReason

new dynamo if known

NoLongerObjector (Maybe peer) TraceCsjReason

new objector if known

SentJumpInstruction (Point blk)

jump target

Instances

Instances details
(StandardHash blk, Show peer) ⇒ Show (TraceEventCsj peer blk) Source # 
Instance details

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

Methods

showsPrecIntTraceEventCsj peer blk → ShowS #

showTraceEventCsj peer blk → String #

showList ∷ [TraceEventCsj peer blk] → ShowS #

data TraceEventDbf peer Source #

Events due to the centralized Devoted BlockFetch logic

Constructors

RotatedDynamo peer peer 

Instances

Instances details
Show peer ⇒ Show (TraceEventDbf peer) Source # 
Instance details

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

Methods

showsPrecIntTraceEventDbf peer → ShowS #

showTraceEventDbf peer → String #

showList ∷ [TraceEventDbf peer] → ShowS #

getDynamo ∷ ∀ (m ∷ TypeType) peer blk. MonadSTM m ⇒ ChainSyncClientHandleCollection peer m blk → STM m (Maybe (peer, ChainSyncClientHandle m blk)) Source #

Find the dynamo in a TVar containing a map of handles. Returns then handle of the dynamo, or Nothing if there is none.

makeContext Source #

Arguments

∷ ∀ (m ∷ TypeType) peer blk. MonadSTM m 
ChainSyncClientHandleCollection peer m blk 
SlotNo 
Tracer m (TraceEventCsj peer blk)

The size of jumps, in number of slots.

STM m (Context m peer blk) 

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

Create the callbacks for a given peer.

noJumping ∷ ∀ (m ∷ TypeType) blk. MonadSTM m ⇒ Jumping m blk Source #

No-op implementation of CSJ

registerClient Source #

Arguments

∷ ∀ blk (m ∷ TypeType) peer. (LedgerSupportsProtocol blk, IOLike m) 
GsmState

the GSM state as of when the node connected to the upstream peer

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, Maybe (TraceEventCsj 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.

Note [Updating the CSJ State when the GSM State Changes]:

The GsmState argument to this function is the only way that the state of the GSM influences CSJ. In particular, when the GSM state changes, the CSJ state does not need any updates whatsoever. That is remarkable enough to deserve some explanation.

  • The GsmState argument to this function merely causes a new client to be immediately disengaged if the GSM is currently in CaughtUp. Otherwise, CSJ will initialize that peer as a Jumper instead of running full ChainSync (unless they happen to be immediately promoted to Dynamo, eg they're the first upstream peer).
  • The transition into CaughtUp does not raise any design questions. The GSM only makes that transition when all peers are idle, and an idle peer will have already disengaged from CSJ. So CSJ doesn't need to react to this transition.
  • The GSM only transitions out of CaughtUp if the tip of its selection is much older than expected (eg 20 minutes). There are many possible explanations for why that could have happened, so it's not obvious what is the best reaction to that transition. This is the interesting case.

The relevant high-level assumption is that in the moment the GSM exits the CaughtUp state, either (i) the node has no proper upstream peers or (ii) the node's selection is out-of-date but not by a huge amount.

  • If the node has no peers, then the CSJ state doesn't need any updates: all of its state is peer-specific. This is anticipated as the main reason the CSJ will leave CaughtUp: eg when the node process was asleep because the user closed the laptop lid overnight.
  • If the node still has peers, then note that they are already disengaged from CSJ, since the GSM was in CaughtUp. The only reason to re-engage them would be to prevent unnecessary load on them. The key design decision here is that the potential load the node's current peers might be able to avoid if they re-engage CSJ from is not worth the extra complexity in CSJ. It's only ~20min worth of ChainSync headers. And if the node hadn't been, eg, asleep last ~20min, those peers would have all sent those headers anyway---the only difference is that the load arrives in a burst.

One key remark: the transition out of CaughtUp does (elsewhere) re-enable the LoP, the LoE, and the GDD, and they apply to all peers regardless of whether those peers are disengaged from CSJ. So security is not directly relevant to this question---recall that CSJ is merely an optimization to avoid excess load on honest upstream peers.

rotateDynamo ∷ (Ord peer, LedgerSupportsProtocol blk, MonadSTM m) ⇒ Tracer m (TraceEventDbf peer) → ChainSyncClientHandleCollection peer m blk → peer → m () Source #

Elects a new dynamo by demoting the given dynamo (and the objector if there is one) to a jumper, moving the peer to the end of the queue of chain sync handles and electing a new dynamo.

It does nothing if there is no other engaged peer to elect or if the given peer is not the dynamo.

unregisterClient ∷ ∀ (m ∷ TypeType) peer blk. (MonadSTM m, Ord peer, LedgerSupportsProtocol blk) ⇒ PeerContext m peer blk → STM m (Maybe (TraceEventCsj peer blk)) 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.