LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_bank.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 131 191 68.6 %
Date: 2025-10-13 04:42:14 Functions: 44 3050 1.4 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_runtime_fd_bank_h
       2             : #define HEADER_fd_src_flamenco_runtime_fd_bank_h
       3             : 
       4             : #include "../types/fd_types.h"
       5             : #include "../leaders/fd_leaders.h"
       6             : #include "../features/fd_features.h"
       7             : #include "../rewards/fd_epoch_rewards.h"
       8             : #include "../stakes/fd_stake_delegations.h"
       9             : #include "../stakes/fd_vote_states.h"
      10             : #include "../fd_rwlock.h"
      11             : #include "fd_blockhashes.h"
      12             : #include "sysvar/fd_sysvar_cache.h"
      13             : #include "../../ballet/lthash/fd_lthash.h"
      14             : 
      15             : FD_PROTOTYPES_BEGIN
      16             : 
      17           9 : #define FD_BANKS_MAGIC (0XF17EDA2C7EBA2450) /* FIREDANCER BANKS V0 */
      18             : 
      19             : /* TODO: Some optimizations, cleanups, future work:
      20             :    1. Simple data types (ulong, int, etc) should be stored as their
      21             :       underlying type instead of a byte array.
      22             :    2. Perhaps make the query/modify scoping more explicit. Right now,
      23             :       the caller is free to use the API wrong if there are no locks.
      24             :       Maybe just expose a different API if there are no locks?
      25             :    3. Rename locks to suffix with _query_locking and _query_locking_end
      26             :    4. Don't templatize out more complex types.
      27             :   */
      28             : 
      29             : /* A fd_bank_t struct is the representation of the bank state on Solana
      30             :    for a given block. More specifically, the bank state corresponds to
      31             :    all information needed during execution that is not stored on-chain,
      32             :    but is instead cached in a validator's memory. Each of these bank
      33             :    fields are repesented by a member of the fd_bank_t struct.
      34             : 
      35             :    Management of fd_bank_t structs must be fork-aware: the state of each
      36             :    fd_bank_t must be based on the fd_bank_t of it's parent block. This
      37             :    state is managed by the fd_banks_t struct.
      38             : 
      39             :    In order to support fork-awareness, there are several key features
      40             :    that fd_banks_t and fd_bank_t MUST support:
      41             :    1. Query for any non-rooted block's bank: create a fast lookup
      42             :       from bank index to bank
      43             :    2. Be able to create a new bank for a given block from the bank of
      44             :       that block's parent and maintain some tree-like structure to
      45             :       track the parent-child relationships: copy the contents from a
      46             :       parent bank into a child bank.
      47             :    3. Prune the set of active banks to keep the root updated as the
      48             :       network progresses: free resources of fd_bank_t structs that
      49             :       are are not direct descendants of the root bank (remove parents
      50             :       and any competing lineages).
      51             :    4. Each bank will have field(s) that are concurrently read/write
      52             :       from multiple threads: add read-write locks to the fields that are
      53             :       concurrently written to.
      54             :    5. In practice, a bank state for a given block can be very large and
      55             :       not all of the fields are written to every block. Therefore, it can
      56             :       be very expensive to copy the entire bank state for a given block
      57             :       each time a bank is created. In order to avoid large memcpys, we
      58             :       can use a CoW mechanism for certain fields.
      59             :    6. In a similar vein, some fields are very large and are not written
      60             :       to very often, and are only read at the epoch boundary. The most
      61             :       notable example is the stake delegations cache. In order to handle
      62             :       this, we can use a delta-based approach where each bank only has
      63             :       a delta of the stake delegations. The root bank will own the full
      64             :       set of stake delegations. This means that the deltas are only
      65             :       applied to the root bank as each bank gets rooted. If the caller
      66             :       needs to access the full set of stake delegations for a given
      67             :       bank, they can assemble the full set of stake delegations by
      68             :       applying all of the deltas from the current bank and all of its
      69             :       ancestors up to the root bank.
      70             : 
      71             :   Each field of a fd_bank_t has a pre-specified set of fields including
      72             :     - name: the name of the field
      73             :     - footprint: the size of the field in bytes
      74             :     - align: the alignment of the field
      75             :     - CoW: whether the field is CoW
      76             :     - has_lock: whether the field has a rw-lock
      77             :     - type: type of the field
      78             : 
      79             :   fd_banks_t is represented by a left-child, right-sibling n-ary tree
      80             :   (inspired by fd_ghost) to keep track of the parent-child fork tree.
      81             :   The underlying data structure is a pool of fd_bank_t structs.  Banks
      82             :   are then accessed via an index into the bank pool (bank index).
      83             : 
      84             :   NOTE: The reason fd_banks_t is keyed by bank index and not by slot is
      85             :   to handle block equivocation: if there are two different blocks for
      86             :   the same slot, we need to be able to differentiate and handle both
      87             :   blocks against different banks.  As mentioned above, the bank index is
      88             :   just an index into the bank pool.  The caller is responsible for
      89             :   establishing a mapping from the bank index (which is managed by
      90             :   fd_banks_t) and runtime state (e.g. slot number).
      91             : 
      92             :   Most of the fields a fd_bank_t are templatized and can support CoW
      93             :   sematics or locking semantics.
      94             : 
      95             :   Each field in fd_bank_t that is not CoW is laid out contiguously in
      96             :   the fd_bank_t struct as simple uchar buffers. This allows for a simple
      97             :   memcpy to clone the bank state from a parent to a child.
      98             : 
      99             :   Each field that is CoW has its own memory pool. The memory
     100             :   corresponding to the field is not located in the fd_bank_t struct and
     101             :   is instead represented by a pool index and a dirty flag. If the field
     102             :   is modified, then the dirty flag is set, and an element of the pool
     103             :   is acquired and the data is copied over from the parent pool idx.
     104             : 
     105             :   Not all fields in the bank are templatized: stake_delegations and
     106             :   the cost_tracker.
     107             : 
     108             :   Currently, there is a delta-based field, fd_stake_delegations_t.
     109             :   Each bank stores a delta-based representation in the form of an
     110             :   aligned uchar buffer.  The full state is stored in fd_banks_t also as
     111             :   a uchar buffer which corresponds to the full state of stake
     112             :   delegations for the current root.  fd_banks_t also reserves another
     113             :   buffer which can store the full state of the stake delegations.
     114             : 
     115             :   The cost tracker is allocated from a pool.  The lifetime of a cost
     116             :   tracker element starts when the bank is linked to a parent with a
     117             :   call to fd_banks_clone_from_parent() which makes the bank replayable.
     118             :   The lifetime of a cost tracker element ends when the bank is marked
     119             :   dead or when the bank is frozen.
     120             : 
     121             :   So, when a bank is cloned from a parent, the non CoW fields are copied
     122             :   over and the CoW fields just copy over a pool index. The CoW behavior
     123             :   is completely abstracted away from the caller as callers have to
     124             :   query/modify fields using specific APIs.
     125             : 
     126             :   The memory for the banks is based off of two bounds:
     127             :   1. the max number of unrooted blocks at any given time. Most fields
     128             :      can be bounded by this value.
     129             :   2. the max number of forks that execute through any 1 block.  We bound
     130             :      fields that are only written to at the epoch boundary by
     131             :      the max fork width that can execute through the boundary instead of
     132             :      by the max number of banks.  See fd_banks_footprint() for more
     133             :      details.
     134             : 
     135             :   There are also some important states that a bank can be in:
     136             :   - Initialized: This bank has been created and linked to a parent bank
     137             :     index with a call to fd_banks_new_bank().  However, it is not yet
     138             :     replayable.
     139             :   - Replayable: This bank has inherited state from its parent and now
     140             :     transactions can be executed against it.  For a bank to become
     141             :     replayable, it must've been initialized beforehand.
     142             :   - Dead: This bank has been marked as dead.  This means that the block
     143             :     that this bank is associated with is invalid.  A bank can be marked
     144             :     dead before, during, or after it has finished replaying.  A bank
     145             :     can still be executing transactions while it is marked dead, but it
     146             :     shouldn't be dispatched any more work.
     147             :   - Frozen: This bank has been marked as frozen and no other tasks
     148             :     should be dispatched to it.  Any bank-specific resources will be
     149             :     released (e.g. cost tracker element).  A bank can be marked frozen
     150             :     in two cases:
     151             :       1. The bank has finished executing all of its transactions
     152             :       2. The bank has been marked dead and there are no outstanding
     153             :          references to the bank.
     154             :     A bank can only be copied from a parent bank
     155             :     (fd_banks_clone_from_parent) if the parent bank has been frozen.
     156             :     The program will crash if this invariant is violated.
     157             : 
     158             :   NOTE: An important invariant is that if a templatized field is CoW,
     159             :   then it must have a rw-lock.
     160             : 
     161             :   NOTE: Another important invariant is that if a templatized field is
     162             :   limiting its fork width, then it must be CoW.
     163             : 
     164             :   The usage pattern is as follows:
     165             : 
     166             :    To create an initial bank:
     167             :    fd_bank_t * bank_init = fd_bank_init_bank( banks );
     168             : 
     169             :    To create a new bank:
     170             :    ulong bank_index = fd_banks_new_bank( banks, parent_bank_index );
     171             : 
     172             :    To clone bank from parent banks:
     173             :    fd_bank_t * bank_clone = fd_banks_clone_from_parent( banks, bank_index, parent_bank_index );
     174             : 
     175             :    To advance the root bank
     176             :    fd_bank_t * root_bank = fd_banks_advance_root( banks, bank_index );
     177             : 
     178             :    To query some arbitrary bank:
     179             :    fd_bank_t * bank_query = fd_banks_bank_query( banks, bank_index );
     180             : 
     181             :   To access fields in the bank if a field does not have a lock:
     182             : 
     183             :   fd_struct_t const * field = fd_bank_field_query( bank );
     184             :   OR
     185             :   fd_struct field = fd_bank_field_get( bank );
     186             : 
     187             :   To modify fields in the bank if a field does not have a lock:
     188             : 
     189             :   fd_struct_t * field = fd_bank_field_modify( bank );
     190             :   OR
     191             :   fd_bank_field_set( bank, value );
     192             : 
     193             :   To access fields in the bank if the field has a lock:
     194             : 
     195             :   fd_struct_t const * field = fd_bank_field_locking_query( bank );
     196             :   ... use field ...
     197             :   fd_bank_field_locking_end_query( bank );
     198             : 
     199             :   To modify fields in the bank if the field has a lock:
     200             : 
     201             :   fd_struct_t * field = fd_bank_field_locking_modify( bank );
     202             :   ... use field ...
     203             :   fd_bank_field_locking_end_locking_modify( bank ); */
     204             : 
     205             : /* Define additional fields to the bank struct here. If trying to add
     206             :    a CoW field to the bank, define a pool for it as done below. */
     207             : 
     208             : #define FD_BANKS_ITER(X)                                                                                                                                                                                                                               \
     209             :   /* type,                             name,                        footprint,                                 align,                                      CoW, limit fork width, has lock */                                                          \
     210          18 :   X(fd_blockhashes_t,                  block_hash_queue,            sizeof(fd_blockhashes_t),                  alignof(fd_blockhashes_t),                  0,   0,                0    )  /* Block hash queue */                                       \
     211          18 :   X(fd_fee_rate_governor_t,            fee_rate_governor,           sizeof(fd_fee_rate_governor_t),            alignof(fd_fee_rate_governor_t),            0,   0,                0    )  /* Fee rate governor */                                      \
     212          18 :   X(ulong,                             slot,                        sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Slot */                                                   \
     213          18 :   X(ulong,                             parent_slot,                 sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Parent slot */                                            \
     214          18 :   X(ulong,                             capitalization,              sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Capitalization */                                         \
     215          18 :   X(ulong,                             lamports_per_signature,      sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Lamports per signature */                                 \
     216          18 :   X(ulong,                             prev_lamports_per_signature, sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Previous lamports per signature */                        \
     217          18 :   X(ulong,                             transaction_count,           sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Transaction count */                                      \
     218          18 :   X(ulong,                             parent_signature_cnt,        sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Parent signature count */                                 \
     219          18 :   X(ulong,                             tick_height,                 sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Tick height */                                            \
     220          18 :   X(ulong,                             max_tick_height,             sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Max tick height */                                        \
     221          18 :   X(ulong,                             hashes_per_tick,             sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Hashes per tick */                                        \
     222          18 :   X(uint128,                           ns_per_slot,                 sizeof(uint128),                           alignof(uint128),                           0,   0,                0    )  /* NS per slot */                                            \
     223          18 :   X(ulong,                             ticks_per_slot,              sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Ticks per slot */                                         \
     224          18 :   X(ulong,                             genesis_creation_time,       sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Genesis creation time */                                  \
     225          18 :   X(double,                            slots_per_year,              sizeof(double),                            alignof(double),                            0,   0,                0    )  /* Slots per year */                                         \
     226          18 :   X(fd_inflation_t,                    inflation,                   sizeof(fd_inflation_t),                    alignof(fd_inflation_t),                    0,   0,                0    )  /* Inflation */                                              \
     227          18 :   X(ulong,                             total_epoch_stake,           sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Total epoch stake */                                      \
     228           9 :                                                                                                                                                                                           /* This is only used for the get_epoch_stake syscall. */     \
     229           9 :                                                                                                                                                                                           /* If we are executing in epoch E, this is the total */      \
     230           9 :                                                                                                                                                                                           /* stake at the end of epoch E-1. */                         \
     231          18 :   X(ulong,                             block_height,                sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Block height */                                           \
     232          18 :   X(ulong,                             execution_fees,              sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Execution fees */                                         \
     233          18 :   X(ulong,                             priority_fees,               sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Priority fees */                                          \
     234          18 :   X(ulong,                             signature_count,             sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Signature count */                                        \
     235          18 :   X(fd_hash_t,                         poh,                         sizeof(fd_hash_t),                         alignof(fd_hash_t),                         0,   0,                0    )  /* PoH */                                                    \
     236          18 :   X(fd_sol_sysvar_last_restart_slot_t, last_restart_slot,           sizeof(fd_sol_sysvar_last_restart_slot_t), alignof(fd_sol_sysvar_last_restart_slot_t), 0,   0,                0    )  /* Last restart slot */                                      \
     237          18 :   X(fd_cluster_version_t,              cluster_version,             sizeof(fd_cluster_version_t),              alignof(fd_cluster_version_t),              0,   0,                0    )  /* Cluster version */                                        \
     238          18 :   X(fd_hash_t,                         bank_hash,                   sizeof(fd_hash_t),                         alignof(fd_hash_t),                         0,   0,                0    )  /* Bank hash */                                              \
     239          18 :   X(fd_hash_t,                         prev_bank_hash,              sizeof(fd_hash_t),                         alignof(fd_hash_t),                         0,   0,                0    )  /* Previous bank hash */                                     \
     240          18 :   X(fd_hash_t,                         genesis_hash,                sizeof(fd_hash_t),                         alignof(fd_hash_t),                         0,   0,                0    )  /* Genesis hash */                                           \
     241          18 :   X(fd_epoch_schedule_t,               epoch_schedule,              sizeof(fd_epoch_schedule_t),               alignof(fd_epoch_schedule_t),               0,   0,                0    )  /* Epoch schedule */                                         \
     242          18 :   X(fd_rent_t,                         rent,                        sizeof(fd_rent_t),                         alignof(fd_rent_t),                         0,   0,                0    )  /* Rent */                                                   \
     243          84 :   X(fd_lthash_value_t,                 lthash,                      sizeof(fd_lthash_value_t),                 alignof(fd_lthash_value_t),                 0,   0,                1    )  /* LTHash */                                                 \
     244          18 :   X(fd_sysvar_cache_t,                 sysvar_cache,                sizeof(fd_sysvar_cache_t),                 alignof(fd_sysvar_cache_t),                 0,   0,                0    )  /* Sysvar cache */                                           \
     245           9 :                                                                                                                                                                                           /* then there can be 100k unique leaders in the worst */     \
     246           9 :                                                                                                                                                                                           /* case. We also can assume 432k slots per epoch. */         \
     247          18 :   X(fd_features_t,                     features,                    sizeof(fd_features_t),                     alignof(fd_features_t),                     0,   0,                0    )  /* Features */                                               \
     248          18 :   X(ulong,                             txn_count,                   sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Transaction count */                                      \
     249          18 :   X(ulong,                             nonvote_txn_count,           sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Nonvote transaction count */                              \
     250          18 :   X(ulong,                             failed_txn_count,            sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Failed transaction count */                               \
     251          18 :   X(ulong,                             nonvote_failed_txn_count,    sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Nonvote failed transaction count */                       \
     252          18 :   X(ulong,                             total_compute_units_used,    sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Total compute units used */                               \
     253          18 :   X(ulong,                             slots_per_epoch,             sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Slots per epoch */                                        \
     254          18 :   X(ulong,                             shred_cnt,                   sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Shred count */                                            \
     255          18 :   X(ulong,                             epoch,                       sizeof(ulong),                             alignof(ulong),                             0,   0,                0    )  /* Epoch */                                                  \
     256          18 :   X(int,                               has_identity_vote,           sizeof(int),                               alignof(int),                               0,   0,                0    )  /* Has identity vote */                                      \
     257         387 :   X(fd_epoch_rewards_t,                epoch_rewards,               FD_EPOCH_REWARDS_FOOTPRINT,                FD_EPOCH_REWARDS_ALIGN,                     1,   1,                1    )  /* Epoch rewards */                                          \
     258         387 :   X(fd_epoch_leaders_t,                epoch_leaders,               FD_EPOCH_LEADERS_MAX_FOOTPRINT,            FD_EPOCH_LEADERS_ALIGN,                     1,   1,                1    )  /* Epoch leaders. If our system supports 100k vote accs, */  \
     259         387 :   X(fd_vote_states_t,                  vote_states,                 FD_VOTE_STATES_FOOTPRINT,                  FD_VOTE_STATES_ALIGN,                       1,   0,                1    )  /* Vote states for all vote accounts as of epoch E if */     \
     260          42 :                                                                                                                                                                                           /* epoch E is the one that is currently being executed */    \
     261         387 :   X(fd_vote_states_t,                  vote_states_prev,            FD_VOTE_STATES_FOOTPRINT,                  FD_VOTE_STATES_ALIGN,                       1,   1,                1    )  /* Vote states for all vote accounts as of of the end of */  \
     262          42 :                                                                                                                                                                                           /* epoch E-1 if epoch E is currently being executed */       \
     263         387 :   X(fd_vote_states_t,                  vote_states_prev_prev,       FD_VOTE_STATES_FOOTPRINT,                  FD_VOTE_STATES_ALIGN,                       1,   1,                1    )  /* Vote states for all vote accounts as of the end of */     \
     264             :                                                                                                                                                                                           /* epoch E-2 if epoch E is currently being executed */
     265             : 
     266             : /* Invariant Every CoW field must have a rw-lock */
     267             : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock)                                              \
     268             :   FD_STATIC_ASSERT( (cow == 1 && has_lock == 1        ) || (cow == 0            ),  CoW fields must have a rw-lock ); \
     269             :   FD_STATIC_ASSERT( (cow == 1 && limit_fork_width == 1) || (limit_fork_width == 0), CoW must be 1 if limit_fork_width is 1 );
     270             :   FD_BANKS_ITER(X)
     271             : #undef X
     272             : 
     273             : /* If a member of the bank is CoW then it needs a corresponding pool
     274             :    which is defined here. If a type if not a CoW then it does not need
     275             :    to be in a pool and is laid out contigiously in the bank struct. */
     276             : 
     277             : /* Declare a pool object wrapper for all CoW fields. */
     278             : #define HAS_COW_1(name, footprint, align)                    \
     279             :   static const ulong fd_bank_##name##_align     = align;     \
     280             :   static const ulong fd_bank_##name##_footprint = footprint; \
     281             :                                                              \
     282             :   struct fd_bank_##name {                                    \
     283             :     ulong next;                                              \
     284             :     uchar data[footprint]__attribute__((aligned(align)));    \
     285             :   };                                                         \
     286             :   typedef struct fd_bank_##name fd_bank_##name##_t;
     287             : 
     288             : /* Do nothing if CoW is not enabled. */
     289             : #define HAS_COW_0(name, footprint, align)
     290             : 
     291             : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     292             :   HAS_COW_##cow(name, footprint, align)
     293             :   FD_BANKS_ITER(X)
     294             : 
     295             : struct fd_bank_cost_tracker {
     296             :   ulong next;
     297             :   uchar data[FD_COST_TRACKER_FOOTPRINT] __attribute__((aligned(FD_COST_TRACKER_ALIGN)));
     298             : };
     299             : typedef struct fd_bank_cost_tracker fd_bank_cost_tracker_t;
     300             : 
     301             : #undef X
     302             : #undef HAS_COW_0
     303             : #undef HAS_COW_1
     304             : 
     305             : #define POOL_NAME fd_bank_epoch_leaders_pool
     306         204 : #define POOL_T    fd_bank_epoch_leaders_t
     307             : #include "../../util/tmpl/fd_pool.c"
     308             : 
     309             : #define POOL_NAME fd_bank_epoch_rewards_pool
     310         183 : #define POOL_T    fd_bank_epoch_rewards_t
     311             : #include "../../util/tmpl/fd_pool.c"
     312             : 
     313             : #define POOL_NAME fd_bank_vote_states_pool
     314         198 : #define POOL_T    fd_bank_vote_states_t
     315             : #include "../../util/tmpl/fd_pool.c"
     316             : 
     317             : #define POOL_NAME fd_bank_vote_states_prev_pool
     318         213 : #define POOL_T    fd_bank_vote_states_prev_t
     319             : #include "../../util/tmpl/fd_pool.c"
     320             : 
     321             : #define POOL_NAME fd_bank_vote_states_prev_prev_pool
     322         183 : #define POOL_T    fd_bank_vote_states_prev_prev_t
     323             : #include "../../util/tmpl/fd_pool.c"
     324             : 
     325             : #define POOL_NAME fd_bank_cost_tracker_pool
     326         417 : #define POOL_T    fd_bank_cost_tracker_t
     327             : #include "../../util/tmpl/fd_pool.c"
     328             : 
     329         201 : #define FD_BANK_FLAGS_INIT              (0x00000001UL) /* Initialized.  Not yet replayable. */
     330          66 : #define FD_BANK_FLAGS_REPLAYABLE        (0x00000002UL) /* Replayable. */
     331          72 : #define FD_BANK_FLAGS_FROZEN            (0x00000004UL) /* Frozen.  We finished replaying or because it was a snapshot/genesis loaded bank. */
     332          42 : #define FD_BANK_FLAGS_DEAD              (0x00000008UL) /* Dead.  We stopped replaying it before we could finish it (e.g. invalid block or pruned minority fork). */
     333          42 : #define FD_BANK_FLAGS_ROOTED            (0x00000010UL) /* Rooted.  Part of the consnensus root fork.  */
     334           0 : #define FD_BANK_FLAGS_EXEC_RECORDING    (0x00000100UL) /* Enable execution recording. */
     335             : 
     336             : /* As mentioned above, the overall layout of the bank struct:
     337             :    - Fields used for internal pool/bank management
     338             :    - Non-Cow fields
     339             :    - CoW fields
     340             :    - Locks for CoW fields
     341             : 
     342             :    The CoW fields are laid out contiguously in the bank struct.
     343             :    The locks for the CoW fields are laid out contiguously after the
     344             :    CoW fields.
     345             : 
     346             :    (r) Field is owned by the replay tile, and should be updated only by
     347             :        the replay tile.
     348             : */
     349             : 
     350             : struct fd_bank {
     351             : 
     352             :   /* Fields used for internal pool and bank management */
     353             :   ulong idx;         /* current fork idx of the bank (synchronized with the pool index) */
     354             :   ulong next;        /* reserved for internal use by pool and fd_banks_advance_root */
     355             :   ulong parent_idx;  /* index of the parent in the node pool */
     356             :   ulong child_idx;   /* index of the left-child in the node pool */
     357             :   ulong sibling_idx; /* index of the right-sibling in the node pool */
     358             :   ulong flags;       /* (r) keeps track of the state of the bank, as well as some configurations */
     359             : 
     360             :   /* Define non-templatized types here. */
     361             : 
     362             :   /* Cost tracker. */
     363             : 
     364             :   ulong       cost_tracker_pool_idx;
     365             :   ulong       cost_tracker_pool_offset;
     366             :   fd_rwlock_t cost_tracker_lock;
     367             : 
     368             :   /* Stake delegations delta. */
     369             : 
     370             :   uchar       stake_delegations_delta[FD_STAKE_DELEGATIONS_DELTA_FOOTPRINT] __attribute__((aligned(FD_STAKE_DELEGATIONS_ALIGN)));
     371             :   int         stake_delegations_delta_dirty;
     372             :   fd_rwlock_t stake_delegations_delta_lock;
     373             : 
     374             :   ulong refcnt; /* (r) reference count on the bank, see replay for more details */
     375             : 
     376             :   fd_txncache_fork_id_t txncache_fork_id; /* fork id used by the txn cache */
     377             : 
     378             :   /* Timestamps written and read only by replay */
     379             : 
     380             :   long first_fec_set_received_nanos;
     381             :   long preparation_begin_nanos;
     382             :   long first_transaction_scheduled_nanos;
     383             :   long last_transaction_finished_nanos;
     384             : 
     385             :   /* First, layout all non-CoW fields contiguously. This is done to
     386             :      allow for cloning the bank state with a simple memcpy. Each
     387             :      non-CoW field is just represented as a byte array. */
     388             : 
     389             :   struct {
     390             : 
     391             :   #define HAS_COW_1(type, name, footprint, align)
     392             : 
     393             :   #define HAS_COW_0(type, name, footprint, align) \
     394             :     uchar name[footprint] __attribute__((aligned(align)));
     395             : 
     396             :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     397             :     HAS_COW_##cow(type, name, footprint, align)
     398             :   FD_BANKS_ITER(X)
     399             :   #undef X
     400             :   #undef HAS_COW_0
     401             :   #undef HAS_COW_1
     402             : 
     403             :   } non_cow;
     404             : 
     405             :   /* Now, layout all information needed for CoW fields. These are only
     406             :      copied when explicitly requested by the caller. The field's data
     407             :      is located at teh pool idx in the pool. If the dirty flag has been
     408             :      set, then the element has been copied over for this bank. */
     409             : 
     410             :   #define HAS_COW_1(type, name, footprint, align) \
     411             :     int   name##_dirty;                           \
     412             :     ulong name##_pool_idx;                        \
     413             :     ulong name##_pool_offset;                     \
     414             :     ulong name##_pool_lock_offset;
     415             : 
     416             :   #define HAS_COW_0(type, name, footprint, align)
     417             : 
     418             :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     419             :     HAS_COW_##cow(type, name, footprint, align)
     420             :   FD_BANKS_ITER(X)
     421             :   #undef X
     422             :   #undef HAS_COW_0
     423             :   #undef HAS_COW_1
     424             : 
     425             :   /* Now emit locks for all fields that need a rwlock. */
     426             : 
     427             :   #define HAS_LOCK_1(type, name, footprint, align) \
     428             :     fd_rwlock_t name##_lock;
     429             : 
     430             :   #define HAS_LOCK_0(type, name, footprint, align) /* Do nothing for these. */
     431             : 
     432             :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     433             :     HAS_LOCK_##has_lock(type, name, footprint, align)
     434             :   FD_BANKS_ITER(X)
     435             :   #undef X
     436             :   #undef HAS_LOCK_0
     437             :   #undef HAS_LOCK_1
     438             : 
     439             : };
     440             : typedef struct fd_bank fd_bank_t;
     441             : 
     442             : #define HAS_COW_1(type, name, footprint, align)                                  \
     443             : static inline void                                                               \
     444         495 : fd_bank_set_##name##_pool( fd_bank_t * bank, fd_bank_##name##_t * bank_pool ) {  \
     445         495 :   void * bank_pool_mem = fd_bank_##name##_pool_leave( bank_pool );               \
     446         495 :   if( FD_UNLIKELY( !bank_pool_mem ) ) {                                          \
     447           0 :     FD_LOG_CRIT(( "Failed to leave bank pool" ));                                \
     448           0 :   }                                                                              \
     449         495 :   bank->name##_pool_offset = (ulong)bank_pool_mem - (ulong)bank;                 \
     450         495 : }                                                                                \
     451             : static inline fd_bank_##name##_t *                                               \
     452         123 : fd_bank_get_##name##_pool( fd_bank_t * bank ) {                                  \
     453         123 :   return fd_bank_##name##_pool_join( (uchar *)bank + bank->name##_pool_offset ); \
     454         123 : }                                                                                \
     455             : static inline void                                                               \
     456         495 : fd_bank_set_##name##_pool_lock( fd_bank_t * bank, fd_rwlock_t * rwlock ) {       \
     457         495 :   bank->name##_pool_lock_offset = (ulong)rwlock - (ulong)bank;                   \
     458         495 : }                                                                                \
     459             : static inline fd_rwlock_t *                                                      \
     460          36 : fd_bank_get_##name##_pool_lock( fd_bank_t * bank ) {                             \
     461          36 :   return (fd_rwlock_t *)( (uchar *)bank + bank->name##_pool_lock_offset );       \
     462          36 : }
     463             : #define HAS_COW_0(type, name, footprint, align) /* Do nothing for these. */
     464             : 
     465             : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     466             :   HAS_COW_##cow(type, name, footprint, align)
     467             : FD_BANKS_ITER(X)
     468             : #undef X
     469             : #undef HAS_COW_0
     470             : #undef HAS_COW_1
     471             : 
     472             : /* Do the same setup for the cost tracker pool. */
     473             : 
     474             : static inline void
     475         108 : fd_bank_set_cost_tracker_pool( fd_bank_t * bank, fd_bank_cost_tracker_t * cost_tracker_pool ) {
     476         108 :   void * cost_tracker_pool_mem = fd_bank_cost_tracker_pool_leave( cost_tracker_pool );
     477         108 :   if( FD_UNLIKELY( !cost_tracker_pool_mem ) ) {
     478           0 :     FD_LOG_CRIT(( "Failed to leave cost tracker pool" ));
     479           0 :   }
     480         108 :   bank->cost_tracker_pool_offset = (ulong)cost_tracker_pool_mem - (ulong)bank;
     481         108 : }
     482             : 
     483             : static inline fd_bank_cost_tracker_t *
     484         267 : fd_bank_get_cost_tracker_pool( fd_bank_t * bank ) {
     485         267 :   return fd_bank_cost_tracker_pool_join( (uchar *)bank + bank->cost_tracker_pool_offset );
     486         267 : }
     487             : 
     488             : /* fd_bank_t is the alignment for the bank state. */
     489             : 
     490             : ulong
     491             : fd_bank_align( void );
     492             : 
     493             : /* fd_bank_t is the footprint for the bank state. This does NOT
     494             :    include the footprint for the CoW state. */
     495             : 
     496             : ulong
     497             : fd_bank_footprint( void );
     498             : 
     499             : /**********************************************************************/
     500             : /* fd_banks_t is the main struct used to manage the bank state.  It can
     501             :    be used to query/modify/clone/publish the bank state.
     502             : 
     503             :    fd_banks_t contains some metadata to a pool to manage the banks.
     504             :    It also contains pointers to the CoW pools.
     505             : 
     506             :    The data is laid out contiguously in memory starting from fd_banks_t;
     507             :    this can be seen in fd_banks_footprint(). */
     508             : 
     509             : #define POOL_NAME fd_banks_pool
     510         435 : #define POOL_T    fd_bank_t
     511             : #include "../../util/tmpl/fd_pool.c"
     512             : 
     513             : struct fd_banks {
     514             :   ulong       magic;           /* ==FD_BANKS_MAGIC */
     515             :   ulong       max_total_banks; /* Maximum number of banks */
     516             :   ulong       max_fork_width;  /* Maximum fork width executing through
     517             :                                   any given slot. */
     518             :   ulong       root_idx;        /* root idx */
     519             : 
     520             :   /* This lock is only used to serialize banks fork tree reads with
     521             :      respect to fork tree writes.  In other words, tree traversals
     522             :      cannot happen at the same time as a tree pruning operation or a
     523             :      tree insertion operation.  So the public APIs on banks take either
     524             :      a read lock or a write lock depending on what they do on the fork
     525             :      tree.  For example, publishing takes a write lock, and bank lookups
     526             :      take a read lock.  Notably, individual banks can still be
     527             :      concurrently accessed or modified, and this lock does not offer
     528             :      synchronization on individual fields within a bank. */
     529             :   fd_rwlock_t rwlock;
     530             : 
     531             :   ulong       pool_offset;     /* offset of pool from banks */
     532             : 
     533             :   ulong       cost_tracker_pool_offset; /* offset of cost tracker pool from banks */
     534             : 
     535             :   /* stake_delegations_root will be the full state of stake delegations
     536             :      for the current root. It can get updated in two ways:
     537             :      1. On boot the snapshot will be directly read into the rooted
     538             :         stake delegations because we assume that any and all snapshots
     539             :         are a rooted slot.
     540             :      2. Calls to fd_banks_publish() will apply all of the stake
     541             :         delegation deltas from each of the banks that are about to be
     542             :         published.  */
     543             : 
     544             :   uchar stake_delegations_root[FD_STAKE_DELEGATIONS_FOOTPRINT] __attribute__((aligned(FD_STAKE_DELEGATIONS_ALIGN)));
     545             : 
     546             :   /* stake_delegations_frontier is reserved memory that can represent
     547             :      the full state of stake delegations for the current frontier. This
     548             :      is done by taking the stake_delegations_root and applying all of
     549             :      the deltas from the current bank and all of its ancestors up to the
     550             :      root bank. */
     551             : 
     552             :   uchar stake_delegations_frontier[FD_STAKE_DELEGATIONS_FOOTPRINT] __attribute__((aligned(FD_STAKE_DELEGATIONS_ALIGN)));
     553             : 
     554             :   /* Layout all CoW pools. */
     555             : 
     556             :   #define HAS_COW_1(type, name, footprint, align)                   \
     557             :     ulong       name##_pool_offset; /* offset of pool from banks */ \
     558             :     fd_rwlock_t name##_pool_lock;   /* lock for the pool */
     559             : 
     560             :   #define HAS_COW_0(type, name, footprint, align) /* Do nothing for these. */
     561             : 
     562             :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     563             :     HAS_COW_##cow(type, name, footprint, align)
     564             :   FD_BANKS_ITER(X)
     565             :   #undef X
     566             :   #undef HAS_COW_0
     567             :   #undef HAS_COW_1
     568             : };
     569             : typedef struct fd_banks fd_banks_t;
     570             : 
     571             : /* Bank accesssors. Different accessors are emitted for different types
     572             :    depending on if the field has a lock or not. */
     573             : 
     574             : #define HAS_LOCK_1(type, name)                                     \
     575             :   type const * fd_bank_##name##_locking_query( fd_bank_t * bank ); \
     576             :   void fd_bank_##name##_end_locking_query( fd_bank_t * bank );     \
     577             :   type * fd_bank_##name##_locking_modify( fd_bank_t * bank );      \
     578             :   void fd_bank_##name##_end_locking_modify( fd_bank_t * bank );
     579             : 
     580             : #define HAS_LOCK_0(type, name)                                   \
     581             :   type const * fd_bank_##name##_query( fd_bank_t const * bank ); \
     582             :   type * fd_bank_##name##_modify( fd_bank_t * bank );
     583             : 
     584             : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     585             :   void fd_bank_##name##_set( fd_bank_t * bank, type value );             \
     586             :   type fd_bank_##name##_get( fd_bank_t const * bank );                   \
     587             :   HAS_LOCK_##has_lock(type, name)
     588             : FD_BANKS_ITER(X)
     589             : #undef X
     590             : 
     591             : /* Define accessor and mutator functions for the non-templatized
     592             :    fields. */
     593             : 
     594             : static inline fd_cost_tracker_t *
     595           0 : fd_bank_cost_tracker_locking_modify( fd_bank_t * bank ) {
     596           0 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_bank_get_cost_tracker_pool( bank );
     597           0 :   if( FD_UNLIKELY( bank->cost_tracker_pool_idx==fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool ) ) ) {
     598           0 :     return NULL;
     599           0 :   }
     600           0 :   uchar * cost_tracker_mem = fd_bank_cost_tracker_pool_ele( cost_tracker_pool, bank->cost_tracker_pool_idx )->data;
     601           0 :   if( FD_UNLIKELY( !cost_tracker_mem ) ) {
     602           0 :     FD_LOG_CRIT(( "invariant violation: cost tracker memory is null" ));
     603           0 :   }
     604           0 :   fd_rwlock_write( &bank->cost_tracker_lock );
     605           0 :   return fd_type_pun( cost_tracker_mem );
     606           0 : }
     607             : 
     608             : static inline void
     609           0 : fd_bank_cost_tracker_end_locking_modify( fd_bank_t * bank ) {
     610           0 :   fd_rwlock_unwrite( &bank->cost_tracker_lock );
     611           0 : }
     612             : 
     613             : static inline fd_cost_tracker_t const *
     614           0 : fd_bank_cost_tracker_locking_query( fd_bank_t * bank ) {
     615           0 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_bank_get_cost_tracker_pool( bank );
     616           0 :   if( FD_UNLIKELY( bank->cost_tracker_pool_idx==fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool ) ) ) {
     617           0 :     return NULL;
     618           0 :   }
     619           0 :   uchar * cost_tracker_mem = fd_bank_cost_tracker_pool_ele( cost_tracker_pool, bank->cost_tracker_pool_idx )->data;
     620           0 :   if( FD_UNLIKELY( !cost_tracker_mem ) ) {
     621           0 :     FD_LOG_CRIT(( "invariant violation: cost tracker memory is null" ));
     622           0 :   }
     623           0 :   fd_rwlock_read( &bank->cost_tracker_lock );
     624           0 :   return fd_type_pun_const( cost_tracker_mem );
     625           0 : }
     626             : 
     627             : static inline void
     628           0 : fd_bank_cost_tracker_end_locking_query( fd_bank_t * bank ) {
     629           0 :   fd_rwlock_unread( &bank->cost_tracker_lock );
     630           0 : }
     631             : 
     632             : #undef HAS_LOCK_0
     633             : #undef HAS_LOCK_1
     634             : 
     635             : /* Each bank has a fd_stake_delegations_t object which is delta-based.
     636             :    The usage pattern is the same as other bank fields:
     637             :    1. fd_bank_stake_dleegations_delta_locking_modify( bank ) will return
     638             :       a mutable pointer to the stake delegations delta object. If the
     639             :       caller has not yet initialized the delta object, then it will
     640             :       be initialized. Because it is a delta it is not copied over from
     641             :       a parent bank.
     642             :    2. fd_bank_stake_delegations_delta_locking_query( bank ) will return
     643             :       a const pointer to the stake delegations delta object. If the
     644             :       delta object has not been initialized, then NULL is returned.
     645             :    3. fd_bank_stake_delegations_delta_locking_end_modify( bank ) will
     646             :       release the write lock on the object.
     647             :    4. fd_bank_stake_delegations_delta_locking_end_query( bank ) will
     648             :       release a read lock on the object.
     649             : */
     650             : 
     651             : static inline fd_stake_delegations_t *
     652          15 : fd_bank_stake_delegations_delta_locking_modify( fd_bank_t * bank ) {
     653          15 :   fd_rwlock_write( &bank->stake_delegations_delta_lock );
     654          15 :   if( !bank->stake_delegations_delta_dirty ) {
     655          15 :     bank->stake_delegations_delta_dirty = 1;
     656          15 :     fd_stake_delegations_new( bank->stake_delegations_delta, FD_STAKE_DELEGATIONS_MAX_PER_SLOT, 1 );
     657          15 :   }
     658          15 :   return fd_stake_delegations_join( bank->stake_delegations_delta );
     659          15 : }
     660             : 
     661             : static inline void
     662          15 : fd_bank_stake_delegations_delta_end_locking_modify( fd_bank_t * bank ) {
     663          15 :   fd_rwlock_unwrite( &bank->stake_delegations_delta_lock );
     664          15 : }
     665             : 
     666             : static inline fd_stake_delegations_t *
     667           0 : fd_bank_stake_delegations_delta_locking_query( fd_bank_t * bank ) {
     668           0 :   fd_rwlock_read( &bank->stake_delegations_delta_lock );
     669           0 :   return bank->stake_delegations_delta_dirty ? fd_stake_delegations_join( bank->stake_delegations_delta ) : NULL;
     670           0 : }
     671             : 
     672             : static inline void
     673           0 : fd_bank_stake_delegations_delta_end_locking_query( fd_bank_t * bank ) {
     674           0 :   fd_rwlock_unread( &bank->stake_delegations_delta_lock );
     675           0 : }
     676             : 
     677             : /* fd_bank_stake_delegations_frontier_query() will return a pointer to
     678             :    the full stake delegations for the current frontier. The caller is
     679             :    responsible that there are no concurrent readers or writers to
     680             :    the stake delegations returned by this function.
     681             : 
     682             :    Under the hood, the function copies the rooted stake delegations and
     683             :    applies all of the deltas for the direct ancestry from the current
     684             :    bank up to the rooted bank to the copy. */
     685             : 
     686             : fd_stake_delegations_t *
     687             : fd_bank_stake_delegations_frontier_query( fd_banks_t * banks,
     688             :                                           fd_bank_t *  bank );
     689             : 
     690             : /* fd_banks_stake_delegations_root_query() will return a pointer to the
     691             :    full stake delegations for the current root. This function should
     692             :    only be called on boot. */
     693             : 
     694             : fd_stake_delegations_t *
     695             : fd_banks_stake_delegations_root_query( fd_banks_t * banks );
     696             : 
     697             : /* Simple getters and setters for the pools/maps in fd_banks_t.  Notably,
     698             :    the pool for the fd_bank_t structs as well as a map and pool pair of
     699             :    the CoW structs in the banks. */
     700             : 
     701             : static inline fd_bank_t *
     702         405 : fd_banks_get_bank_pool( fd_banks_t const * banks ) {
     703         405 :   return fd_banks_pool_join( ((uchar *)banks + banks->pool_offset) );
     704         405 : }
     705             : 
     706             : static inline void
     707             : fd_banks_set_bank_pool( fd_banks_t * banks,
     708           9 :                         fd_bank_t *  bank_pool ) {
     709           9 :   void * bank_pool_mem = fd_banks_pool_leave( bank_pool );
     710           9 :   if( FD_UNLIKELY( !bank_pool_mem ) ) {
     711           0 :     FD_LOG_CRIT(( "Failed to leave bank pool" ));
     712           0 :   }
     713           9 :   banks->pool_offset = (ulong)bank_pool_mem - (ulong)banks;
     714           9 : }
     715             : 
     716             : #define HAS_COW_1(type, name, footprint, align)                                    \
     717             : static inline fd_bank_##name##_t *                                                 \
     718         708 : fd_banks_get_##name##_pool( fd_banks_t * banks ) {                                 \
     719         708 :   return fd_bank_##name##_pool_join( (uchar *)banks + banks->name##_pool_offset ); \
     720         708 : }                                                                                  \
     721             : static inline void                                                                 \
     722          45 : fd_banks_set_##name##_pool( fd_banks_t * banks, fd_bank_##name##_t * bank_pool ) { \
     723          45 :   void * bank_pool_mem = fd_bank_##name##_pool_leave( bank_pool );                 \
     724          45 :   if( FD_UNLIKELY( !bank_pool_mem ) ) {                                            \
     725           0 :     FD_LOG_CRIT(( "Failed to leave bank pool" ));                                  \
     726           0 :   }                                                                                \
     727          45 :   banks->name##_pool_offset = (ulong)bank_pool_mem - (ulong)banks;                 \
     728          45 : }
     729             : 
     730             : #define HAS_COW_0(type, name, footprint, align) /* Do nothing for these. */
     731             : 
     732             : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     733             :   HAS_COW_##cow(type, name, footprint, align)
     734             : FD_BANKS_ITER(X)
     735             : #undef X
     736             : #undef HAS_COW_0
     737             : #undef HAS_COW_1
     738             : 
     739             : static inline fd_bank_cost_tracker_t *
     740         120 : fd_banks_get_cost_tracker_pool( fd_banks_t * banks ) {
     741         120 :   return fd_bank_cost_tracker_pool_join( (uchar *)banks + banks->cost_tracker_pool_offset );
     742         120 : }
     743             : 
     744             : static inline void
     745             : fd_banks_set_cost_tracker_pool( fd_banks_t *             banks,
     746           9 :                                 fd_bank_cost_tracker_t * cost_tracker_pool ) {
     747           9 :   void * cost_tracker_pool_mem = fd_bank_cost_tracker_pool_leave( cost_tracker_pool );
     748           9 :   if( FD_UNLIKELY( !cost_tracker_pool_mem ) ) {
     749           0 :     FD_LOG_CRIT(( "Failed to leave cost tracker pool" ));
     750           0 :   }
     751           9 :   banks->cost_tracker_pool_offset = (ulong)cost_tracker_pool_mem - (ulong)banks;
     752           9 : }
     753             : 
     754             : /* fd_banks_root() and fd_banks_root_const() returns a non-const and
     755             :    const pointer to the root bank respectively. */
     756             : 
     757             : FD_FN_PURE static inline fd_bank_t const *
     758           0 : fd_banks_root_const( fd_banks_t const * banks ) {
     759           0 :   return fd_banks_pool_ele_const( fd_banks_get_bank_pool( banks ), banks->root_idx );
     760           0 : }
     761             : 
     762             : FD_FN_PURE static inline fd_bank_t *
     763          42 : fd_banks_root( fd_banks_t * banks ) {
     764          42 :   return fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), banks->root_idx );
     765          42 : }
     766             : 
     767             : /* fd_banks_align() returns the alignment of fd_banks_t */
     768             : 
     769             : ulong
     770             : fd_banks_align( void );
     771             : 
     772             : /* fd_banks_footprint() returns the footprint of fd_banks_t.  This
     773             :    includes the struct itself but also the footprint for all of the
     774             :    pools.
     775             : 
     776             :    The footprint of fd_banks_t is determined by the total number
     777             :    of banks that the bank manages.  This is an analog for the max number
     778             :    of unrooted blocks the bank can manage at any given time.
     779             : 
     780             :    We can also further bound the memory footprint of the banks by the
     781             :    max width of forks that can exist at any given time.  The reason for
     782             :    this is that there are several large CoW structs that are only
     783             :    written to during the epoch boundary (e.g. epoch_rewards,
     784             :    epoch_stakes, etc.).  These structs are read-only afterwards. This
     785             :    means if we also bound the max number of forks that can execute
     786             :    through the epoch boundary, we can bound the memory footprint of
     787             :    the banks. */
     788             : 
     789             : ulong
     790             : fd_banks_footprint( ulong max_total_banks,
     791             :                     ulong max_fork_width );
     792             : 
     793             : /* fd_banks_new() creates a new fd_banks_t struct.  This function lays
     794             :    out the memory for all of the constituent fd_bank_t structs and
     795             :    pools depending on the max_total_banks and the max_fork_width for a
     796             :    given block. */
     797             : 
     798             : void *
     799             : fd_banks_new( void * mem,
     800             :               ulong  max_total_banks,
     801             :               ulong  max_fork_width );
     802             : 
     803             : /* fd_banks_join() joins a new fd_banks_t struct. */
     804             : 
     805             : fd_banks_t *
     806             : fd_banks_join( void * mem );
     807             : 
     808             : /* fd_banks_leave() leaves a bank. */
     809             : 
     810             : void *
     811             : fd_banks_leave( fd_banks_t * banks );
     812             : 
     813             : /* fd_banks_delete() deletes a bank. */
     814             : 
     815             : void *
     816             : fd_banks_delete( void * shmem );
     817             : 
     818             : /* fd_banks_init_bank() initializes a new bank in the bank manager.
     819             :    This should only be used during bootup. This returns an initial
     820             :    fd_bank_t with the corresponding bank index set to 0. */
     821             : 
     822             : fd_bank_t *
     823             : fd_banks_init_bank( fd_banks_t * banks );
     824             : 
     825             : /* fd_banks_get_bank_idx returns a bank for a given bank index. */
     826             : 
     827             : static inline fd_bank_t *
     828             : fd_banks_bank_mem_query( fd_banks_t * banks,
     829           0 :                          ulong        bank_idx ) {
     830           0 :   return fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), bank_idx );
     831           0 : }
     832             : 
     833             : static inline fd_bank_t *
     834             : fd_banks_bank_query( fd_banks_t * banks,
     835         126 :                      ulong        bank_idx ) {
     836         126 :   fd_bank_t * bank = fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), bank_idx );
     837         126 :   return (bank->flags&FD_BANK_FLAGS_INIT) ? bank : NULL;
     838         126 : }
     839             : 
     840             : static inline fd_bank_t *
     841             : fd_banks_get_parent( fd_banks_t * banks,
     842           0 :                      fd_bank_t *  bank ) {
     843           0 :   return fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), bank->parent_idx );
     844           0 : }
     845             : 
     846             : /* fd_banks_clone_from_parent() clones a bank from a parent bank.
     847             :    This function links the child bank to its parent bank and copies
     848             :    over the data from the parent bank to the child.  This function
     849             :    assumes that the child and parent banks both have been allocated.
     850             :    The parent bank must be frozen and the child bank must be initialized
     851             :    but not yet used.
     852             : 
     853             :    A more detailed note: not all of the data is copied over and this
     854             :    is a shallow clone.  All of the CoW fields are not copied over and
     855             :    will only be done so if the caller explicitly calls
     856             :    fd_bank_{*}_modify().  This naming was chosen to emulate the
     857             :    semantics of the Agave client. */
     858             : 
     859             : fd_bank_t *
     860             : fd_banks_clone_from_parent( fd_banks_t * banks,
     861             :                             ulong        bank_idx,
     862             :                             ulong        parent_bank_idx );
     863             : 
     864             : /* fd_banks_advance_root() advances the root bank to the bank manager.
     865             :    This should only be used when a bank is no longer needed and has no
     866             :    active refcnts.  This will prune off the bank from the bank manager.
     867             :    It returns the new root bank.  An invariant of this function is that
     868             :    the new root bank should be a child of the current root bank.
     869             : 
     870             :    All banks that are ancestors or siblings of the new root bank will be
     871             :    cancelled and their resources will be released back to the pool. */
     872             : 
     873             : fd_bank_t const *
     874             : fd_banks_advance_root( fd_banks_t * banks,
     875             :                        ulong        bank_idx );
     876             : 
     877             : /* fd_bank_clear_bank() clears the contents of a bank. This should ONLY
     878             :    be used with banks that have no children and should only be used in
     879             :    testing and fuzzing.
     880             : 
     881             :    This function will memset all non-CoW fields to 0.
     882             : 
     883             :    For all CoW fields, we will reset the indices to its parent. */
     884             : 
     885             : void
     886             : fd_banks_clear_bank( fd_banks_t * banks,
     887             :                      fd_bank_t *  bank );
     888             : 
     889             : /* fd_banks_advance_root_prepare returns the highest block that can be
     890             :    safely advanced between the current root of the fork tree and the
     891             :    target block.  See the note on safe publishing for more details.  In
     892             :    general, a node in the fork tree can be pruned if:
     893             :    (1) the node itself can be pruned, and
     894             :    (2) all subtrees (except for the one on the rooted fork) forking off
     895             :        of the node can be pruned.
     896             :    The highest publishable block is the highest block on the rooted fork
     897             :    where the above is true, or the rooted child block of such if there
     898             :    is one.
     899             : 
     900             :    This function assumes that the given target block has been rooted by
     901             :    consensus.  It will mark every block on the rooted fork as rooted, up
     902             :    to the given target block.  It will also mark minority forks as dead.
     903             : 
     904             :    Highest advanceable block is written to the out pointer.  Returns 1
     905             :    if the advanceable block can be advanced beyond the current root.
     906             :    Returns 0 if no such block can be found.  We will ONLY advance our
     907             :    advanceable_bank_idx to a child of the current root.  In order to
     908             :    advance to the target bank, fd_banks_advance_root_prepare() must be
     909             :    called repeatedly. */
     910             : 
     911             : int
     912             : fd_banks_advance_root_prepare( fd_banks_t * banks,
     913             :                                ulong        target_bank_idx,
     914             :                                ulong *      advanceable_bank_idx_out );
     915             : 
     916             : /* fd_banks_mark_bank_dead marks the current bank (and all of its
     917             :    descendants) as dead.  The caller is still responsible for handling
     918             :    the behavior of the dead bank correctly. */
     919             : 
     920             : void
     921             : fd_banks_mark_bank_dead( fd_banks_t * banks,
     922             :                          fd_bank_t *  bank );
     923             : 
     924             : /* fd_banks_mark_bank_frozen marks the current bank as frozen.  This
     925             :    should be done when the bank is no longer being updated: it should be
     926             :    done at the end of a slot.  This also releases the memory for the
     927             :    cost tracker which only has to be persisted from the start of a slot
     928             :    to the end. */
     929             : 
     930             : void
     931             : fd_banks_mark_bank_frozen( fd_banks_t * banks,
     932             :                            fd_bank_t *  bank );
     933             : 
     934             : /* fd_banks_new_bank reserves a bank index for a new bank.  New bank
     935             :    indicies should always be available.  After this function is called,
     936             :    the bank will be linked to its parent bank, but not yet replayable.
     937             :    After a call to fd_banks_clone_from_parent, the bank will be
     938             :    replayable.  This assumes that there is a parent bank which exists
     939             :    and the there are available bank indices in the bank pool. */
     940             : 
     941             : fd_bank_t *
     942             : fd_banks_new_bank( fd_banks_t * banks,
     943             :                    ulong        parent_bank_idx,
     944             :                    long         now );
     945             : 
     946             : 
     947             : /* fd_banks_is_full returns 1 if the banks are full, 0 otherwise. */
     948             : 
     949             : static inline int
     950           0 : fd_banks_is_full( fd_banks_t * banks ) {
     951           0 :   return fd_banks_pool_free( fd_banks_get_bank_pool( banks ) )==0UL;
     952           0 : }
     953             : 
     954             : /* fd_banks_validate does validation on the banks struct to make sure
     955             :    that there are no corruptions/invariant violations.  It returns 0
     956             :    if no issues have been detected and 1 otherwise.
     957             : 
     958             :    List of checks that the function currently performs:
     959             :    1. CoW fields have not acquired more elements than the max amount of
     960             :       allocated banks.
     961             :    (Add more checks as needed here)
     962             : */
     963             : 
     964             : int
     965             : fd_banks_validate( fd_banks_t * banks );
     966             : 
     967             : FD_PROTOTYPES_END
     968             : 
     969             : #endif /* HEADER_fd_src_flamenco_runtime_fd_bank_h */

Generated by: LCOV version 1.14