LCOV - code coverage report
Current view: top level - discof/replay - fd_replay_tile_private.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 4 0.0 %
Date: 2026-08-14 04:54:57 Functions: 0 0 -

          Line data    Source code
       1             : #ifndef HEADER_fd_src_discof_replay_fd_replay_tile_private_h
       2             : #define HEADER_fd_src_discof_replay_fd_replay_tile_private_h
       3             : 
       4             : #include "fd_vote_tracker.h"
       5             : #include "../../disco/topo/fd_wksp_mon.h"
       6             : #include "../../disco/store/fd_store.h"
       7             : #include "../../disco/bundle/fd_bundle_crank.h"
       8             : #include "../../disco/keyguard/fd_keyswitch.h"
       9             : #include "../../disco/node_info/fd_node_info.h"
      10             : #include "../../discof/reasm/fd_reasm.h"
      11             : #include "../../discof/replay/fd_sched.h"
      12             : #include "../../flamenco/capture/fd_capture_ctx.h"
      13             : #include "../../flamenco/genesis/fd_genesis_parse.h"
      14             : #include "../../flamenco/leaders/fd_multi_epoch_leaders.h"
      15             : #include "../../flamenco/progcache/fd_progcache.h"
      16             : #include "../../flamenco/runtime/fd_bank.h"
      17             : #include "../../flamenco/runtime/fd_txncache.h"
      18             : #include "../../flamenco/runtime/tests/fd_dump_pb.h"
      19             : #include <stdio.h>
      20             : 
      21             : struct fd_replay_in_link {
      22             :   fd_wksp_t * mem;
      23             :   ulong       chunk0;
      24             :   ulong       wmark;
      25             :   ulong       mtu;
      26             : };
      27             : 
      28             : typedef struct fd_replay_in_link fd_replay_in_link_t;
      29             : 
      30             : struct fd_replay_out_link {
      31             :   ulong       idx;
      32             :   fd_wksp_t * mem;
      33             :   ulong       chunk0;
      34             :   ulong       wmark;
      35             :   ulong       chunk;
      36             : };
      37             : 
      38             : typedef struct fd_replay_out_link fd_replay_out_link_t;
      39             : 
      40             : /* fd_block_id_map is a simple map of block-ids to bank indices.  The
      41             :    map sits on top of an array of fd_block_id_ele_t.  This serves as a
      42             :    translation layer between block ids to bank indices.  The data
      43             :    array is indexed by bank index and the latest observed merkle root
      44             :    for the bank index is stored in the array.  Once the block id has
      45             :    been observed, the entry is keyed by the latest merkle root (aka the
      46             :    block id). */
      47             : 
      48             : struct fd_block_id_ele {
      49             :   fd_hash_t latest_mr;
      50             :   uint      latest_fec_idx;
      51             :   int       block_id_seen;
      52             :   ulong     slot;
      53             :   ulong     bank_seq;
      54             :   ulong     next_;
      55             : };
      56             : typedef struct fd_block_id_ele fd_block_id_ele_t;
      57             : 
      58             : #define MAP_NAME               fd_block_id_map
      59             : #define MAP_ELE_T              fd_block_id_ele_t
      60             : #define MAP_KEY_T              fd_hash_t
      61           0 : #define MAP_KEY                latest_mr
      62           0 : #define MAP_NEXT               next_
      63           0 : #define MAP_KEY_EQ(k0,k1)      (!memcmp((k0),(k1), sizeof(fd_hash_t)))
      64           0 : #define MAP_KEY_HASH(key,seed) (fd_hash((seed),(key),sizeof(fd_hash_t)))
      65             : #include "../../util/tmpl/fd_map_chain.c"
      66             : 
      67             : struct fd_replay_tile {
      68             :   fd_wksp_t * wksp;
      69             : 
      70             :   uint rng_seed;
      71             :   fd_rng_t rng[ 1 ];
      72             : 
      73             :   fd_progcache_join_t progcache[1];
      74             :   fd_wksp_mon_t       progcache_wksp_mon[1];
      75             : 
      76             :   fd_accdb_t *    accdb;
      77             :   fd_txncache_t * txncache;
      78             :   fd_store_t *    store;
      79             :   fd_banks_t *    banks;
      80             : 
      81             :   /* This flag is 1 If we have seen a vote signature that our node has
      82             :      sent out get rooted at least one time.  The value is 0 otherwise.
      83             :      We can't become leader and pack blocks until this flag has been
      84             :      set.  This parallels the Agave 'has_new_vote_been_rooted'. */
      85             :   int identity_vote_rooted;
      86             :   int wait_for_vote_to_start_leader;
      87             : 
      88             :   /* wfs_enabled is 1 if the validator is booted in
      89             :      wait_for_supermajority mode. In this mode replay (and, by extension,
      90             :      downstream consumers) is not allowed to make progress until 80% of
      91             :      the cluster has published their ContactInfo in Gossip with a
      92             :      shred version matching expected_shred_version. When this happens,
      93             :      wfs_complete will be set to 1. */
      94             :   int   wfs_enabled;
      95             :   int   wfs_complete;
      96             : 
      97             :   fd_hash_t expected_bank_hash;
      98             : 
      99             :   ulong            blockhash_seed;
     100             :   ulong            reasm_seed;
     101             :   fd_reasm_t     * reasm;
     102             :   fd_reasm_fec_t * reasm_evicted; /* evicted FEC by reasm_insert must be stored in returnable_frag, and then drained in after_credit */
     103             : 
     104             :   fd_sched_t * sched;
     105             :   ulong        in_cnt;
     106             :   ulong        execrp_idle_cnt;
     107             : 
     108             :   ulong                vote_tracker_seed;
     109             :   fd_vote_tracker_t *  vote_tracker;
     110             : 
     111             :   uint has_genesis_hash:1;
     112             :   uint has_cluster_type:1;
     113             :   uint has_genesis_timestamp:1;
     114             :   uint has_expected_genesis_timestamp:1;
     115             : 
     116             :   char         genesis_path[ PATH_MAX ];
     117             :   fd_hash_t    genesis_hash[1];
     118             :   fd_genesis_t genesis[1];
     119             :   ulong        cluster_type;
     120             :   ulong        genesis_timestamp;
     121             :   ulong        expected_genesis_timestamp;
     122             : 
     123             :   ulong          hard_fork_cnt;
     124             :   fd_hard_fork_t hard_forks[ FD_HARD_FORKS_MAX ];
     125             : 
     126             :   ushort expected_shred_version;
     127             :   ushort ipecho_shred_version;
     128             : 
     129             :   ulong enable_features_cnt;
     130             :   char  enable_features[ 16 ][ FD_BASE58_ENCODED_32_SZ ];
     131             : 
     132             :   /* A note on publishing ...
     133             : 
     134             :      The watermarks are used to publish our fork-aware structures.  For
     135             :      example, store, banks, and txncache need to be published to release
     136             :      resources occupied by rooted or dead blocks.  In general,
     137             :      publishing has the effect of pruning forks in those structures,
     138             :      indicating that it is ok to release the memory being occupied by
     139             :      the blocks on said forks.  Tower is responsible for informing us of
     140             :      the latest block on the consensus rooted fork.  As soon as we can,
     141             :      we should move the published root as close as possible to the
     142             :      latest consensus root, publishing/pruning everything on the fork
     143             :      tree along the way.  That is, all the blocks that directly descend
     144             :      from the current published root (inclusive) to the new published
     145             :      root (exclusive) on the rooted fork, as well as all the minority
     146             :      forks that branch from said blocks.
     147             : 
     148             :      Ideally, we'd move the published root to the consensus root
     149             :      immediately upon receiving a new consensus root.  However, that's
     150             :      not always safe to do.  One thing we need to be careful about is
     151             :      making sure that there are no more users/consumers of
     152             :      soon-to-be-pruned blocks, lest a use-after-free occurs.  This can
     153             :      be done by using a reference counter for each block.  Any
     154             :      concurrent activity, such as transaction execution in the exec
     155             :      tiles, should retain a refcnt on the block for as
     156             :      long as it needs access to the shared fork-aware structures related
     157             :      to that block.  Eventually, refcnt on a given block will drop down
     158             :      to 0 as the block either finishes replaying or gets marked as dead,
     159             :      and any other tile that has retained a refcnt on the block releases
     160             :      it.  At that point, it becomes a candidate for pruning.  The key to
     161             :      safe publishing then becomes figuring out how far we could advance
     162             :      the published root, such that every minority fork branching off of
     163             :      blocks in between the current published root (inclusive) and the
     164             :      new published root (exclusive) is safe to be pruned.  This is a
     165             :      straightforward tree traversal, where if a block B on the rooted
     166             :      fork has refcnt 0, and all minority forks branching off of B also
     167             :      have refcnt 0, then B is safe to be pruned.  We advance the
     168             :      published root to the farthest consecutively prunable block on the
     169             :      rooted fork.  Note that reasm presents the replay tile with a clean
     170             :      view of the world where every block is chained off of a parent
     171             :      block.  So there are no orpahned/dangling tree nodes to worry
     172             :      about.  The world is a nice single tree as far as replay is
     173             :      concerned.
     174             : 
     175             :      In the following fork tree, every node is a block and the number in
     176             :      parentheses is the refcnt on the block.  The chain marked with
     177             :      double slashes is the rooted fork.  Suppose the published root is
     178             :      at block P, and consensus root is at block T.  We can't publish
     179             :      past block P because Q has refcnt 1.
     180             : 
     181             : 
     182             :           P(0)
     183             :         /    \\
     184             :       Q(1)    A(0)
     185             :             / ||  \
     186             :         X(0) B(0)  C(0)
     187             :        /      || \
     188             :       Y(0)   M(0) R(0)
     189             :             / ||   /  \
     190             :         D(2) T(0) J(0) L(0)
     191             :               ||
     192             :               ..
     193             :               ..
     194             :               ..
     195             :               ||
     196             :       blocks we might be actively replaying
     197             : 
     198             : 
     199             :      When refcnt on Q drops to 0, we would be able to advance the
     200             :      published root to block M, because blocks P, A, and B, as well as
     201             :      all subtrees branching off of them, have refcnt 0, and therefore
     202             :      can be pruned.  Block M itself cannot be pruned yet because its
     203             :      child block D has refcnt 2.  After publishing/pruning, the fork
     204             :      tree would be:
     205             : 
     206             : 
     207             :              M(0)
     208             :             / ||
     209             :         D(2) T(0)
     210             :               ||
     211             :               ..
     212             :               ..
     213             :               ..
     214             :               ||
     215             :       blocks we might be actively replaying
     216             : 
     217             : 
     218             :      As a result, the shared fork-aware structures can free resources
     219             :      for blocks P, A, B, and all subtrees branching off of them.
     220             : 
     221             :      For the reference counting part, the replay tile is the sole entity
     222             :      that can update the refcnt.  This ensures that all refcnt increment
     223             :      and decrement attempts are serialized at the replay tile, and that
     224             :      there are no racy resurrection of a soon-to-be-pruned block.  If a
     225             :      refcnt increment request arrives after a block has been pruned,
     226             :      replay simply rejects the request.
     227             : 
     228             :      A note on the implementation of the above ...
     229             : 
     230             :      Upon receiving a new consensus root, we descend down the rooted
     231             :      fork from the current published root to the new consensus root.  On
     232             :      each node/block of the rooted fork, we do a summation of the refcnt
     233             :      on the block and all the minority fork blocks branching from the
     234             :      block.  If the summation is 0, the block is safe for pruning.  We
     235             :      advance the published root to the far end of the consecutive run of
     236             :      0 refcnt sums originating from the current published root.  On our
     237             :      descent down the minority forks, we also mark any block that hasn't
     238             :      finished replaying as dead, so we don't waste time executing them.
     239             :      No more transactions shall be dispatched for execution from dead
     240             :      blocks.
     241             : 
     242             :      Blocks start out with a refcnt of 0.  Other tiles may send a
     243             :      request to the replay tile for a reference on a block.  The
     244             :      transaction dispatcher is another source of refcnt updates.  On
     245             :      every dispatch of a transaction for block B, we increment the
     246             :      refcnt for B.  And on every transaction finalization, we decrement
     247             :      the refcnt for B.  This means that whenever the refcnt on a block
     248             :      is 0, there is no more reference on that block from the execution
     249             :      pipeline.  While it might be tempting to simply increment the
     250             :      refcnt once when we start replaying a block, and decrement the
     251             :      refcnt once when we finish a block, this more fine-grained refcnt
     252             :      update strategy allows for aborting and potentially immediate
     253             :      pruning of blocks under interleaved block replay.  Upon receiving a
     254             :      new consensus root, we can simply look at the refcnt on minority
     255             :      fork blocks, and a refcnt of 0 would imply that the block is safe
     256             :      for pruning, even if we haven't finished replaying it.  Without the
     257             :      fine-grained refcnt, we would need to first stop dispatching from
     258             :      the aborted block, and then wait for a full drain of the execution
     259             :      pipeline to know for sure that there are no more in-flight
     260             :      transactions executing on the aborted block.  Note that this will
     261             :      allow the refcnt on any block to transiently drop down to 0.  We
     262             :      will not mistakenly prune an actively replaying block, aka a leaf
     263             :      node, that is chaining off of the rooted fork, because the
     264             :      consensus root is always an ancestor of the actively replaying tip.
     265             :      */
     266             :   fd_hash_t consensus_root;          /* The most recent block to have reached max lockout in the tower. */
     267             :   ulong     consensus_root_slot;     /* slot number of the above. */
     268             :   fd_hash_t notified_root;           /* The most recent consensus root sent to sched, RPC, and resolv. */
     269             :   ulong     notified_root_slot;      /* slot number of the above. */
     270             :   fd_bank_t * notified_root_bank;    /* bank held by sched, RPC, and resolv for the notified root. */
     271             :   ulong     published_root_slot;     /* slot number of the published root. */
     272             :   ulong     published_root_bank_idx; /* bank index of the published root. */
     273             : 
     274             :   /* Randomly generated block id for the initial genesis/snapshot slot.
     275             :      Used as a fallback when the snapshot manifest does not contain a
     276             :      block_id (block_id will be populated in Agave 4.1 snapshots and
     277             :      will be required in Agave 4.2). */
     278             : 
     279             :   fd_hash_t initial_block_id;
     280             : 
     281             :   /* The merkle root of the snapshot slot's block.
     282             :      Only present in snapshots generated by Agave >=4.1. */
     283             : 
     284             :   int       has_manifest_block_id;
     285             :   fd_hash_t manifest_block_id;
     286             : 
     287             :   /* We need to maintain a tile-local mapping of block-ids to bank index
     288             :      and vice versa.  This translation layer is needed for conversion
     289             :      since tower operates on block-ids and downstream consumers of FEC
     290             :      sets operate on bank indices.  This mapping must happen both ways:
     291             :      1. tower sends us block ids and we must map them to bank indices.
     292             :      2. when a block is completed, we must map the bank index to a block
     293             :         id to send a slot complete message to tower. */
     294             :   ulong               block_id_len;
     295             :   fd_block_id_ele_t * block_id_arr;
     296             :   ulong               block_id_map_seed;
     297             :   fd_block_id_map_t * block_id_map;
     298             : 
     299             :   /* Capture-related configs */
     300             :   fd_capture_ctx_t *     capture_ctx;
     301             :   FILE *                 capture_file;
     302             :   fd_capture_link_buf_t  cap_repl_out[1];
     303             : 
     304             :   /* Protobuf dumping context for debugging runtime execution and
     305             :      collecting seed corpora. */
     306             :   fd_dump_proto_ctx_t * dump_proto_ctx;
     307             : 
     308             :   /* Whether the runtime has been booted either from snapshot loading
     309             :      or from genesis. */
     310             :   int is_booted;
     311             : 
     312             :   /* Buffer to store vote towers that need to be published to the Tower
     313             :      tile. */
     314             : 
     315             :   fd_multi_epoch_leaders_t * mleaders;
     316             : 
     317             :   int larger_max_cost_per_block;
     318             : 
     319             :   /* When we transition to becoming leader, we can only unbecome leader
     320             :      if we have received a block id from the FEC reassembler, and a
     321             :      message from PoH that the leader slot has ended.  After both of
     322             :      these conditions are met, then we are free to unbecome leader. */
     323             :   uint        is_leader : 1;
     324             :   uint        supports_leader : 1;
     325             :   int         recv_poh;
     326             :   ulong       next_leader_slot;
     327             :   long        next_leader_tickcount;
     328             :   double      tick_per_ns;
     329             :   ulong       highwater_leader_slot;
     330             :   ulong       reset_slot;
     331             : 
     332             :   /* Caught up to the cluster: replay has completed a slot within a few
     333             :      slots of the highest FEC set slot seen from repair (which tracks
     334             :      the turbine tip). */
     335             :   int         caught_up;
     336             :   ulong       catch_up_max_fec_slot;
     337             :   ulong       catch_up_tip_advance_cnt;
     338             :   long        boot_timestamp_nanos;
     339             :   fd_hash_t   reset_block_id;
     340             :   long        reset_timestamp_nanos;
     341             :   fd_bank_t * leader_bank;
     342             : 
     343             :   fd_pubkey_t      identity_pubkey[1];
     344             :   ulong            identity_idx;
     345             :   int              identity_dirty;
     346             : 
     347             :   fd_node_info_box_t * node_info; /* shared */
     348             : 
     349             :   fd_keyswitch_t * keyswitch;
     350             :   int              halt_leader;
     351             : 
     352             :   ulong  resolv_tile_cnt;
     353             : 
     354             :   int in_kind[ 128 ];
     355             :   fd_replay_in_link_t in[ 128 ];
     356             : 
     357             :   fd_replay_out_link_t exec_out[ 1 ];
     358             : 
     359             :   fd_replay_out_link_t replay_out[1];
     360             :   fd_replay_out_link_t snapmk_out[1];
     361             :   ulong admin_out_idx;
     362             : 
     363             :   fd_replay_out_link_t epoch_out[1];
     364             : 
     365             :   /* The rpc tile needs to occasionally own a reference to a live bank.
     366             :      Replay needs to know if the rpc as a consumer is enabled so it can
     367             :      increment the bank's refcnt before publishing bank_idx. */
     368             :   int rpc_enabled;
     369             : 
     370             :   /* For dumping blocks to protobuf. For backtest only. */
     371             :   fd_block_dump_ctx_t * block_dump_ctx;
     372             : 
     373             :   /* We need a few pieces of information to compute the right addresses
     374             :      for bundle crank information that we need to send to pack. */
     375             :   struct {
     376             :     int                   enabled;
     377             :     fd_pubkey_t           vote_account;
     378             :     fd_bundle_crank_gen_t gen[1];
     379             :   } bundle;
     380             : 
     381             :   /* snapshot producer */
     382             :   struct {
     383             :     uint  supported:1;
     384             :     uint  active:1;
     385             :     uint  incremental:1;
     386             :     ulong bank_idx;
     387             :     ulong scheduled_at;
     388             :     ulong full_interval;
     389             :     ulong next_full_slot;
     390             :     ulong incremental_interval;
     391             :     ulong next_incremental_slot;
     392             :     ulong base_slot;
     393             :   } snapmk;
     394             : 
     395             :   struct {
     396             :     ulong      store_query_acquire;
     397             :     ulong      store_query_release;
     398             :     fd_histf_t store_query_wait[1];
     399             :     fd_histf_t store_query_work[1];
     400             :     ulong      store_query_cnt;
     401             :     ulong      store_query_missing_cnt;
     402             :     ulong      store_query_mr;
     403             :     ulong      store_query_missing_mr;
     404             : 
     405             :     ulong slots_total;
     406             :     ulong transactions_total;
     407             : 
     408             :     ulong reasm_latest_slot;
     409             :     ulong reasm_latest_fec_idx;
     410             : 
     411             :     ulong sched_full;
     412             :     ulong reasm_empty;
     413             :     ulong leader_bid_wait;
     414             :     ulong banks_full;
     415             :     ulong storage_root_behind;
     416             :   } metrics;
     417             : 
     418             :   uchar __attribute__((aligned(FD_MULTI_EPOCH_LEADERS_ALIGN))) mleaders_mem[ FD_MULTI_EPOCH_LEADERS_FOOTPRINT ];
     419             : 
     420             :   ulong                runtime_stack_seed;
     421             :   fd_runtime_stack_t * runtime_stack;
     422             : };
     423             : 
     424             : typedef struct fd_replay_tile fd_replay_tile_t;
     425             : 
     426             : #endif /* HEADER_fd_src_discof_replay_fd_replay_tile_private_h */

Generated by: LCOV version 1.14