LCOV - code coverage report
Current view: top level - discof/replay - fd_rdisp.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 394 1108 35.6 %
Date: 2026-11-11 12:40:43 Functions: 15 40 37.5 %

          Line data    Source code
       1             : #include "fd_rdisp.h"
       2             : #include "fd_rdisp_mq.h"
       3             : #include "../../util/fd_hash32.h"
       4             : #include <math.h> /* for the EMA */
       5             : 
       6             : /* The conflict graph that this file builds is not a general DAG, but
       7             :    the union of several special account-conflict graphs.  Each
       8             :    account-conflict graph has special structure:
       9             :                            ---> 3 --
      10             :                           /          \
      11             :                          /            v
      12             :                1  ---> 2 -----> 4 --> 6 ----> 7
      13             :                         \             ^
      14             :                          \           /
      15             :                           ----> 5 --
      16             : 
      17             :    That is, the graph is almost a line, but may have fan-out and fan-in
      18             :    regions.  The tricky part about representing a graph like this
      19             :    without dynamic memory allocation is that nodes may have arbitrary
      20             :    in-degree and out-degree.  Thus, we use a pretty standard trick and
      21             :    use sibling pointers, denoted below with dotted lines.  Each node
      22             :    maintains at most one successor pointer and at most one sibling
      23             :    pointer.  Although not shown below, the sibling pointers are
      24             :    circularly linked, so 5's sibling is 3.  Additionally, though not
      25             :    shown below, the child of the last node (7) stores information about
      26             :    what account address all this is for, which facilitates deleting the
      27             :    last node in the graph.
      28             : 
      29             : 
      30             :                               --> 3 --
      31             :                              /    ^   \
      32             :                             /     :    \
      33             :                            /      V     v
      34             :                  1  ---> 2        4 --> 6 ----> 7
      35             :                                   ^     ^
      36             :                                   :    /
      37             :                                   V   /
      38             :                                   5 --
      39             : 
      40             :    The normal edge 2->3 along with the sibling edge 3..>4 implies a
      41             :    normal edge 2->4.  That then transitively implies an edge 2->5.
      42             : 
      43             :    We want each node to maintain a count of its in-degree so that we
      44             :    know when it can be executed.  The implied edges also count for the
      45             :    in-degree.  In this example, node 1 has in-degree 0, node 6 has
      46             :    in-degree 3, and the rest have in-degree 1.
      47             : 
      48             :    Maintaining each account-conflict graph is relatively easy given the
      49             :    operations we want to support.  Only the details about sibling edges
      50             :    are worth mentioning.  For example, when deleting node 2, we
      51             :    decrement the in-degree count for its successor, and then follow the
      52             :    sibling pointers, decrementing all the in-degree counts as we go to
      53             :    mark the deletion of the implied edges.  Sibling edges also need to
      54             :    be doubly linked, so that e.g. nodes 3 and 5 can be re-linked in O(1)
      55             :    if node 4 is deleted.  They're also circularly linked as well for
      56             :    convenience.
      57             : 
      58             :    When building the graph, we maintain a map of account to the last
      59             :    node that references it, whether that was a read or write, and
      60             :    whether there are any writers to that account in the graph right now.
      61             :    If the new node reads from an account that was last read, the new
      62             :    node becomes a sibling of the last read, with in-degree increased if
      63             :    there are any writers.  Otherwise, it becomes a successor of the node
      64             :    that last referenced the account. */
      65             : 
      66             : /* For a task like this with lots of graph traversal and pointer
      67             :    chasing, performance is typically limited by memory latency.  That
      68             :    means that the more that can fit in cache, the better the
      69             :    performance.  This implementation uses a lot of bit-packing to
      70             :    improve cache footprint. */
      71             : 
      72             : /* The following structs are all very local to this compilation unit,
      73             :    and so they don't have globally acceptable type names (e.g.
      74             :    fd_rdisp_edge_t). */
      75             : 
      76             : #define MAX_ACCT_PER_TXN FD_TXN_ACCT_ADDR_MAX
      77             : FD_STATIC_ASSERT( MAX_ACCT_PER_TXN<=128UL, max_acct_per_txn );
      78             : 
      79             : /* edge_t: Fields typed edge_t represent an edge in one of the parallel
      80             :    account-conflict DAGs.  Each transaction stores a list of all its
      81             :    outgoing edges.  The type is actually a union of bitfield, but C
      82             :    bitfields are gross, so we just do it manually with macros.
      83             :    If the high bit is not set, then the next 23 bits store the
      84             :    destination of the edge, represented by its index in the pool of
      85             :    transactions.  Then the lowest 8 bits store the account index within
      86             :    that transaction of the edge that is part of the same
      87             :    account-specific DAG as this edge.  Because the max depth is 2^23-1,
      88             :    and each transaction can reference FD_TXN_ACCT_ADDR_MAX accounts,
      89             :    the max accounts that can be referenced fits in 30 bits.
      90             : 
      91             :    If the high bit is set, the second highest bit indicates whether it's
      92             :    a pseudo transaction or if the transaction storing this edge_t value
      93             :    is the last in this specific DAG.  Either way, the lower 30 bits of
      94             :    the value are the index.  This index is in the map_pool for the
      95             :    account pubkey for this DAG if it's a final transaction, and an index
      96             :    into the pseudo transaction pool if it's a pseudo-transaction.  See
      97             :    the comments about the hidden edge outgoing from node 7 in the DAG at
      98             :    the top of this file for an example.
      99             :    Because the max depth is 2^23-1, and each transaction can reference
     100             :    128 accounts, the max accounts that can be referenced fits in 30
     101             :    bits.
     102             :    The proper type would be something like
     103             :    typedef union {
     104             :      struct {
     105             :        uint is_special:1; // == 0
     106             :        uint txn_idx:23;
     107             :        uint acct_idx:8;
     108             :      } e;
     109             :      struct {
     110             :        uint is_special:1; // == 1
     111             :        uint is_last:1;    // == 1
     112             :        uint map_pool_idx:30;
     113             :      } last;
     114             :      struct {
     115             :        uint is_special:1; // == 1
     116             :        uint is_last:1;    // == 0
     117             :        uint pseudo_txn_idx:30;
     118             :      } pseudo;
     119             :    } edge_t;
     120             : 
     121             :    */
     122             : typedef uint edge_t;
     123             : 
     124       41305 : #define EDGE_SPECIAL_PTXN     0x80000000U
     125        4689 : #define EDGE_SPECIAL_LAST     0xC0000000U
     126       36616 : #define EDGE_SPECIAL_MASK     0xC0000000U
     127             : 
     128             : #define EDGE_IS_LAST(x)        (((x)&EDGE_SPECIAL_MASK)==EDGE_SPECIAL_LAST)
     129       36616 : #define EDGE_IS_PTXN(x)        (((x)&EDGE_SPECIAL_MASK)==EDGE_SPECIAL_PTXN)
     130           0 : #define EDGE_SPECIAL_IDX(x)    ((x)&(~EDGE_SPECIAL_MASK))
     131             : #define EDGE_SPECIAL_IDX_MAX   (~EDGE_SPECIAL_MASK)
     132        9378 : #define EDGE_MAKE_SPECIAL(k,x) ((x)|FD_CONCAT2(EDGE_SPECIAL_,k))
     133             : 
     134             : FD_STATIC_ASSERT( MAX_ACCT_PER_TXN<=128UL, max_acct_per_txn );
     135             : 
     136             : /* fd_rdisp_txn is the representation of a transaction as a node in the
     137             :    DAG. */
     138             : struct fd_rdisp_txn {
     139             :   /* in_degree: The total number of edges summed across all account DAGs
     140             :      with this node as their destination.  In the worst case, all the
     141             :      other transactions in the pool read from each of the max number of
     142             :      accounts that this transaction writes to,  so there are
     143             :      FD_RDISP_MAX_ACCT_PER_TXN*depth edges that come into this node,
     144             :      which fits in about 30 bits, so we have some room for special
     145             :      values.  If in_degree is one of the following values, then the
     146             :      transaction is: */
     147   993267870 : #define IN_DEGREE_FREE                (UINT_MAX   )
     148           0 : #define IN_DEGREE_UNSTAGED            (UINT_MAX-1U)/* unstaged, not dispatched */
     149           0 : #define IN_DEGREE_DISPATCHED          (UINT_MAX-2U)/* staged,   dispatched */
     150           0 : #define IN_DEGREE_UNSTAGED_DISPATCHED (UINT_MAX-3U)/* unstaged, dispatched */
     151           0 : #define IN_DEGREE_ZOMBIE              (UINT_MAX-4U)/* zombie */
     152             :   /* a transaction that is staged and dispatched is must have an
     153             :      in_degree of 0.  in_degree isn't a meaningful concept for unstaged
     154             :      transactions. */
     155             :   uint    in_degree;
     156             : 
     157             :   /* score: integer part stores how many transactions in the block must
     158             :      have completed before this transaction can be scheduled.  This is
     159             :      useful for transactions marked as serializing.  The fractional part
     160             :      gives some measure of how urgent the transaction is, where lower is
     161             :      more urgent.  This means we can't have more transactions in a block
     162             :      marked as serializing than the first integer that a float cannot
     163             :      represent.  That value is about 16M, which is much higher than the
     164             :      maximum number of transactions in a block, so this is not a
     165             :      problem.  If in the very rare case that there are more than just a
     166             :      few serializing transactions, we will lose a bit of precision for
     167             :      the fractional portion, which makes total sense; if there's not
     168             :      much room for parallel execution, having the optimal parallel
     169             :      execution is not very important. */
     170             :   float   score;
     171             : 
     172             :   /* edge_cnt_etc:
     173             :      0xFFFF0000 (16 bits) for linear block number,
     174             :      0x0000C000 (2 bits) for concurrency lane,
     175             :      0x00003F80 (7 bits) for r_cnt
     176             :      0x0000007F (7 bits) for w_cnt_1.  Transactions have at least one
     177             :      writable account and at most MAX_ACCT_PER_TXN total accounts, so
     178             :      storing w_cnt_1 = w_cnt - 1 fits in 7 bits. */
     179             :   union {
     180             :     uint edge_cnt_etc;
     181             :     /* edge_cnt_etc is only used when the transaction is STAGED and
     182             :        PENDING, READY, or DISPATCHED.  If UNSTAGED or FREE, the next
     183             :        pointer is also here.  It can't be UNSTAGED and FREE at the same
     184             :        time, so there's no conflict with storage there either. */
     185             :     uint unstaged_next;
     186             :     uint free_next;
     187             :     /* If ZOMBIE, pointer back to block is here.  No storage conflict
     188             :        because ZOMBIE is an exclusive state. */
     189             :     uint block_idx;
     190             :   };
     191             : 
     192             : 
     193             :   /* When a transaction writes to an account, it only creates one
     194             :      link.  When a transaction reads from an account, we need the full
     195             :      doubly linked list with its siblings, so it creates 3 edges
     196             :      (child, next, prev).  All edges from writable accounts come first,
     197             :      and we keep track of how many there are.  In the worst case, all
     198             :      accounts are reads, so we size it appropriately. */
     199             :   edge_t edges[3UL*FD_RDISP_MAX_ACCT_PER_TXN]; /* addressed [0, w_cnt_1+1+3*r_cnt) */
     200             : };
     201             : typedef struct fd_rdisp_txn fd_rdisp_txn_t;
     202             : 
     203             : /* To simplify the code, we make fd_rdisp_ptxn_t such that it can be cast
     204             :    as a fd_rdisp_txn_t * and mostly function.  This greatly simplifies
     205             :    the code but is technically UB.  The alias rules say that a write to
     206             :    a field of a pointer of type T1* and a write to a field of a pointer
     207             :    of type T2* should be independent.  To combat this, we need to be
     208             :    careful in each function to either use the ptxn_t * pointer or to
     209             :    immediately cast to txn_t * and only access it via that pointer. */
     210             : struct fd_rdisp_ptxn {
     211             :   /* These fields have the same meaning as in fd_rdisp_txn_t */
     212             :   uint  in_degree;
     213             :   float score;
     214             :   union {
     215             :     uint edge_cnt_etc; /* r_cnt == 1 and w_cnt_1 is 0x7F, but ignored */
     216             :     uint free_next;
     217             :   };
     218             :   /* The whole reason for the difference between a normal transaction
     219             :      and a pseudo-transaction is that this array can be a lot smaller.
     220             :      */
     221             :   edge_t edges[3];
     222             :   /* last maps back to the account for which this pseudo-transaction is an
     223             :      LtHash addition task.  It's normally the same as edges[0], but if a
     224             :      writer is added after this pseudo-transaction is added (for
     225             :      example, in the next block), then edges[0] may have a different
     226             :      value, but we still need to be able to map back to the account
     227             :      address. */
     228             :   edge_t last;
     229             : };
     230             : typedef struct fd_rdisp_ptxn fd_rdisp_ptxn_t;
     231             : 
     232             : #define POOL_NAME  ptxn_pool
     233   954282148 : #define POOL_NEXT  free_next
     234             : #define POOL_IDX_T uint
     235        3023 : #define POOL_T     fd_rdisp_ptxn_t
     236             : #include "../../util/tmpl/fd_pool.c"
     237             : 
     238             : /* Two more definitions:
     239             :    An edge index is an array position within the edges array.  An
     240             :    account index is a position within a transaction's account addresses,
     241             :    reordered so that the writable ones come first.  Since writable
     242             :    accounts use one position in the edges array per account address,
     243             :    these often coincide. */
     244             : 
     245             : 
     246             : /* FOLLOW_EDGE and FOLLOW_EDGE_TXN are helper macros for dealing with
     247             :    edges.  Given an edge_t x, and rdisp disp, FOLLOW_EDGE_TXN
     248             :    returns a pointer to transaction that the edge points to; FOLLOW_EDGE
     249             :    returns a pointer to the (first) edge_t within that transaction that
     250             :    is part of the same DAG as this edge.  FOLLOW_EDGE and
     251             :    FOLLOW_EDGE_TXN must not be called if EDGE_IS_LAST is non-zero.
     252             : 
     253             :    Then the edge index, i.e. the position in the edges array of the
     254             :    (first) edge for an account index is:
     255             :           acct_idx                              if acct_idx<=w_cnt_1
     256             :           w_cnt_1+1 + 3*(acct_idx-w_cnt_1-1)    else.
     257             :   Simplifying gives
     258             :          acct_idx + 2*signed_max( 0, acct_idx-w_cnt_1-1 ).
     259             :   If the edge points to a pseudo-transaction, then __w_cnt_1 is a
     260             :   garbage value, but __idx is forced to 0, which means its value is not
     261             :   used.
     262             :   In doing this calculation, we also basically get for free whether the
     263             :   edge is for a writable account or a readonly account, so we return
     264             :   that as well via the w parameter.  If the child transaction only reads
     265             :   the account address for this DAG, then the next and prev pointers can
     266             :   be accessed using the returned value +1 and +2, respectively. */
     267        9154 : #define FOLLOW_EDGE(disp, x, w) (__extension__({                                                                            \
     268        9154 :         fd_rdisp_t const * __disp = (disp);                                                                                 \
     269        9154 :         uint __e = (x);                                                                                                     \
     270        9154 :         edge_t * __edges = EDGE_IS_PTXN(__e) ? __disp->ptxn_pool[EDGE_SPECIAL_IDX(__e)].edges : __disp->pool[__e>>8].edges; \
     271        9154 :         uint __w_cnt_1   = EDGE_IS_PTXN(__e) ? 0 : __disp->pool[__e>>8].edge_cnt_etc & 0x7FU;                               \
     272        9154 :         uint __idx  = fd_uint_if( EDGE_IS_PTXN(__e), 0U, (__e & 0xFFU) );                                                   \
     273        9154 :         (w) = (!EDGE_IS_PTXN(__e)) & (__idx<=__w_cnt_1);                                                                    \
     274        9154 :         (void)(w);  /* not robust... */                                                                                     \
     275        9154 :         __edges + __idx + 2*fd_int_max( 0, (int)(__idx)-(int)(__w_cnt_1)-1 ); }))
     276             : 
     277           0 : #define FOLLOW_EDGE_TXN(disp, x) (__extension__({                                                                    \
     278           0 :         fd_rdisp_t const * __disp = (disp);                                                                          \
     279           0 :         uint __e = (x);                                                                                              \
     280           0 :         EDGE_IS_PTXN(__e) ? (fd_rdisp_txn_t *)((__disp)->ptxn_pool+EDGE_SPECIAL_IDX(__e)) :                          \
     281           0 :                             ((__disp)->pool+(__e>>8)); }))
     282             : 
     283             : 
     284             : 
     285             : /* The pool and slist are almost the same, but they are used
     286             :    differently, so keep them as different structures for now. */
     287             : 
     288             : #define POOL_NAME     pool
     289        3021 : #define POOL_T        fd_rdisp_txn_t
     290             : #define POOL_IDX_T    uint
     291     3836089 : #define POOL_NEXT     free_next
     292             : #define POOL_SENTINEL 1
     293             : #include "../../util/tmpl/fd_pool.c"
     294             : 
     295             : #define SLIST_NAME unstaged_txn_ll
     296             : #define SLIST_ELE_T fd_rdisp_txn_t
     297           0 : #define SLIST_IDX_T uint
     298           0 : #define SLIST_NEXT  unstaged_next
     299             : #include "../../util/tmpl/fd_slist.c"
     300             : 
     301             : #define DLIST_IDX_T uint
     302           0 : #define DLIST_PREV  edges[0]
     303           0 : #define DLIST_NEXT  edges[1]
     304             : #define DLIST_NAME  zombie_dlist
     305             : #define DLIST_ELE_T fd_rdisp_txn_t
     306             : #include "../../util/tmpl/fd_dlist.c"
     307             : 
     308             : 
     309             : /* ACCT_INFO_STATE: We need to keep just a little metadata about writers
     310             :    in the DAG for an account.  In particular, for correctness, we need
     311             :    to be able to distinguish the case where there are only readers so
     312             :    that we don't increment in_degree when adding a new fd_rdisp_txn.  If
     313             :    we have any writers, the only way to transition into a state where
     314             :    there are only readers is to complete the last writer.  We know we
     315             :    are in this case when the completed node's child doesn't have a
     316             :    child, and the completed node's child is a reader, as indicated by
     317             :    the LAST_REF_WAS_WRITE bit.  LAST_REF_WAS_WRITE also has the
     318             :    advantage of being easy to maintain.  You'd think with those two
     319             :    binary states we'd have used up our 2 bits, but actually, there are
     320             :    only states there, which means we can almost cram in a little more
     321             :    information for free.  In particular, whether we need to create a
     322             :    LtHash addition hash is also very related to ANY_WRITERS and
     323             :    LAST_REF_WAS_WRITE, via the state machine below, which has only 5
     324             :    accessible states out of 8 possible states (we can add a reader or
     325             :    writer from any state. If there are any writers, we can complete the
     326             :    last writer. If it needs an LtHash add pseudo transaction, then we
     327             :    may add a pseudo transaction).
     328             : 
     329             :          add reader
     330             :          |--------|
     331             :          V        |
     332             :       0-----------------
     333             :       | No writers,    |<-------------------------------------------
     334             :       | which implies  |                                           |
     335             :       | LRWW==0 and    |<---------------------------------         |
     336             :       | NEEDS_LTADD==0 |     complete last writer        |         |
     337             :       ------------------                                 |         |
     338             :               |                                          |         |
     339             :           add |             complete last writer         |         |
     340             :        writer |       -----------------------------------+-------  |
     341             :               V       |                                  |      |  |
     342             :       1-----------------        add writer               |      |  |
     343             :       | ANY_WRITERS==1 |<---------------------------|    |      |  |
     344             :    |->| LRWW==1 and    |------------------------    |    |      |  |
     345             :    |  | NEEDS_LTADD==1 |<---|        add pseudo|    |    |      |  |
     346             :    |  ------------------    | add          txn |    |    |      |  |
     347             :    |         ^  |     |     | writer           V    |    |      |  |
     348             :    |         |  |     |-----|          2*--------------------   |  |
     349             :    |     add |  | add                  | Last is pseudo txn |   |  |
     350             :    |  writer |  | reader           |-->| ANY_WRITERS==1     |   |  |
     351             :    |         |  V              add |   | LRWW==0 and        |   |  |
     352             :    |  2-----------------    reader |---| NEEDS_LTADD==0     |   |  |
     353             :    |  | ANY_WRITERS==1 |               ----------------------   |  |
     354             :    |  | LRWW==0 and    |                        ^               |  |
     355             :    |  | NEEDS_LTADD==1 |------------------------|               |  |
     356             :    |  ------------------     add pseudo txn                     |  |
     357             :    |     |        ^  |                                          |  |
     358             :    |     |        |  |                                          |  |
     359             :    |     ----------  |                3------------------       |  |
     360             :    |     add reader  |                | ANY_WRITERS==0  |       |  |
     361             :    |                 ---------------->| LRWW==0 and     |<-------  |
     362             :    |                   complete last  | NEEDS_LTADD==1  |-----------
     363             :    |                      writer      ------------------- add pseudo
     364             :    |                                   |   |        ^       txn
     365             :    |       add writer                  |   |        |
     366             :    |------------------------------------   ----------
     367             :                                            add reader
     368             : 
     369             :    Sadly, we still only have 2 bits, so we collapse the top-left and
     370             :    bottom right states into one state.  The only difference between the
     371             :    two states is whether it is possible to add a pseudo-txn, and that's
     372             :    driven by the rdisp_mq anyway. */
     373             : /*                                                 AW  LRWW   NLT */
     374           0 : #define ACCT_INFO_STATE_NO_WRITERS             0U /*0    0      0  */
     375       12266 : #define ACCT_INFO_STATE_LAST_REF_WAS_WRITE     1U /*1    1      1  */
     376        4689 : #define ACCT_INFO_STATE_LAST_REF_WAS_READ      2U /*1    0      ?  */
     377           0 : #define ACCT_INFO_STATE_NEEDS_PTXN             3U /*0    0      1  */
     378             : 
     379       14067 : #define ACCT_INFO_STATE_IS( val, lane, state) ( (((val)>>(2U*(lane)))&3) == FD_CONCAT2(ACCT_INFO_STATE_, state) )
     380        2888 : #define ACCT_INFO_STATE_SET(val, lane, state) (((val) & (uchar)(~(3U<<(2U*(lane))))) | (uchar)(FD_CONCAT2(ACCT_INFO_STATE_, state)<<(2U*(lane))) )
     381             : 
     382             : 
     383             : /* acct_info_t is a node in a map_chain that contains the metadata for a
     384             :    single account address's conflict graph DAG.  In particular, it
     385             :    contains the information needed to know where to insert a node that
     386             :    reads from or writes to the account.  The objects of this type follow
     387             :    this state machine:
     388             : 
     389             :                  FREE  -----> ACTIVE ----> CACHED
     390             :                                 ^            |
     391             :                                 |-------------
     392             :    When FREE, it is in free_acct_dlist only.  When ACTIVE, it is in
     393             :    acct_map only.  When CACHED, it is in both free_acct_dlist and
     394             :    acct_map, and cached==ACCT_INFO_CACHED.
     395             : */
     396           0 : #define ACCT_INFO_CACHED UINT_MAX
     397             : FD_STATIC_ASSERT( ((FD_RDISP_MAX_DEPTH<<8)|0xFFUL)<(ulong)ACCT_INFO_CACHED, acct_info_cached ); /* no edge_t is the mark */
     398             : 
     399             : struct acct_info {
     400             :   /* key, next, and prev are the map_chain fields. Used in the ACTIVE
     401             :      and CACHED states.  next and prev set to 0 when in the FREE state.
     402             :      Element 0 is a sentinel and isn't inserted to the free_acct_dlist,
     403             :      so this is unambiguous. */
     404             :   fd_acct_addr_t key;
     405             :   uint next;
     406             :   uint prev;
     407             : 
     408             :   union {
     409             :     struct {
     410             :       /* This is effectively a pointer to the last node in the DAG for
     411             :          this pubkey, one for each staging lane.
     412             :          EDGE_IS_LAST(FOLLOW_EDGE(disp, last_reference[i])) is non-zero.
     413             :          */
     414             :       edge_t last_reference[4];
     415             : 
     416             :     }; /* When in the ACTIVE state */
     417             :     struct {
     418             :       uint free_ll_next;
     419             :       uint free_ll_prev;
     420             :       /* cached is ACCT_INFO_CACHED in the CACHED state, which tells a
     421             :          CACHED element in acct_map from an ACTIVE one: it aliases
     422             :          last_reference[2], an edge_t of a transaction index in the
     423             :          ACTIVE state, which has its top bit clear.  Not maintained in
     424             :          the FREE state (next and prev tell FREE from CACHED). */
     425             :       uint cached;
     426             :       /* 4 bytes of padding here */
     427             :     }; /* When not in the ACTIVE state, used by the free_acct_dlist */
     428             :   };
     429             :   /* states: a combination of ACCT_INFO_STATE fields above.  Used when
     430             :      ACTIVE. */
     431             :   uint states:8;
     432             : 
     433             : 
     434             :   /* We want to dispatch the READY transactions in an order that
     435             :      maximizes parallelism, but we also want to be able to start
     436             :      dispatching transactions decently well before we have the full
     437             :      conflict graph.  We can do that because we know that contentious
     438             :      accounts tend to stay contentious and uncontentious accounts tend
     439             :      to stay uncontentious.
     440             : 
     441             :      To accomplish this, we maintain a special EMA.  Let x_i be 1 if
     442             :      transaction i references it and 0 if not.  If we squint and assume
     443             :      the transactions are independent and all drawn from some
     444             :      distribution (which is not true, but that's why we squint), an EMA
     445             :      of x_i estimates the probability that the next transaction
     446             :      references this account.  How we use this value is detailed later.
     447             : 
     448             :      We can't update this value for every pubkey for each transaction,
     449             :      so we maintain it in a lazy way, by applying updates only when we
     450             :      need to read the value, which also happens to be every time we want
     451             :      to add a 1 value to the EMA.  We then just need to maintain the
     452             :      last index i at which x_i was updated and the current value.  We
     453             :      only have 24 bits for the index, which means that we can't maintain
     454             :      it in a fork-aware way, which doesn't seem like a problem.  Also,
     455             :      it's possible it can overflow, and that can result in an incorrect
     456             :      value, but that means the account is only referenced ~ 1/2^24
     457             :      transactions, which is also fine.
     458             : 
     459             :      last_ref is in the domain of global_inserted_txn_cnt.  ema_refs is
     460             :      in [0, 1].  Both fields are used in ACTIVE and CACHED.  Maintaining
     461             :      these fields is actually the main reason CACHED exists. */
     462             :   uint   last_ref:24;
     463             :   float  ema_refs;
     464             : };
     465             : typedef struct acct_info acct_info_t;
     466             : 
     467             : FD_STATIC_ASSERT( sizeof(acct_info_t)==64UL, acct_info_t );
     468             : 
     469             : #define MAP_NAME          acct_map
     470           0 : #define MAP_ELE_T         acct_info_t
     471       14329 : #define MAP_IDX_T         uint
     472             : #define MAP_KEY_T         fd_acct_addr_t
     473       13318 : #define MAP_KEY_HASH(k,s) fd_hash32( (k)->b, (s) )
     474         745 : #define MAP_KEY_EQ(k0,k1) (!memcmp( (k0)->b, (k1)->b, 32UL ))
     475             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     476             : #include "../../util/tmpl/fd_map_chain.c"
     477             : 
     478             : 
     479             : #define DLIST_IDX_T uint
     480   820115985 : #define DLIST_PREV  free_ll_prev
     481   820115985 : #define DLIST_NEXT  free_ll_next
     482             : #define DLIST_NAME  free_dlist
     483             : #define DLIST_ELE_T acct_info_t
     484             : #include "../../util/tmpl/fd_dlist.c"
     485             : 
     486             : 
     487             : struct pending_prq_ele {
     488             :   /* lower score means should be scheduled sooner.  Integer part has a
     489             :      special meaning as explained above. */
     490             :   float score;
     491             : 
     492             :   uint linear_block_number;
     493             :   uint txn_idx;
     494             : 
     495             :   /* It seems like we should be able to get this whole struct in 8
     496             :      bytes, but we're a few bits over.
     497             : 
     498             :      23 bits for txn_idx
     499             :      16 bit for linear_block_number
     500             :      Then the integer part of the score needs 20 bits.  It is bounded by
     501             :      a block's inserted_cnt, which counts pseudo-transactions as well as
     502             :      transactions and is budgeted for up to
     503             :      FD_MAX_TXN_PER_SLOT+2*FD_RDISP_MAX_WRITERS_PER_BLOCK, just over
     504             :      2^19.  But we can't use a custom floating point number. */
     505             : };
     506             : 
     507             : typedef struct pending_prq_ele pending_prq_ele_t;
     508             : 
     509             : 
     510             : #define PRQ_NAME pending_prq
     511        1890 : #define PRQ_T    pending_prq_ele_t
     512             : #define PRQ_EXPLICIT_TIMEOUT 0
     513             : /* returns 1 if x is strictly after y */
     514           0 : #define PRQ_AFTER(x,y) (__extension__( {                                            \
     515           0 :             int cmp0 = (int)(x).linear_block_number - (int)(y).linear_block_number; \
     516           0 :             fd_int_if( cmp0!=0, cmp0>0, (x).score>(y).score );                      \
     517           0 :             }))
     518             : #include "../../util/tmpl/fd_prq.c"
     519             : 
     520             : /* We need yet another data structure to keep track of accounts that
     521             :    need LtHash addition tasks for unstaged transactions.  These are part
     522             :    of a map keyed by (block, acct), as stored in ustlt_key_t so that we
     523             :    can quickly query whether the account address is "new" (in the sense
     524             :    explained in the header).  In addition to possibly being part of the
     525             :    map, these are also part of one of several linked lists: FREE,
     526             :    NEEDED, CREATED.  When in the CREATED state and list, there's a
     527             :    pseudo-txn with a last field pointing to it.  When in the NEEDED
     528             :    state and list, it's part of the map_chain.  Technically FREE could
     529             :    be an slist/fd_pool, but it's easier to reason about correctness when
     530             :    each element is in exactly one of three lists at all times. */
     531             : typedef struct {
     532             :   uint           block_idx;
     533             :   fd_acct_addr_t acct;
     534             : } ustlt_key_t;
     535             : 
     536             : struct ustlt {
     537             :   ustlt_key_t key;
     538             :   uint map_prev;
     539             :   uint map_next;
     540             :   uint ll_prev;
     541             :   uint ll_next;
     542             :   uint ptxn_idx; /* when CREATED */
     543             : };
     544             : typedef struct ustlt ustlt_t;
     545             : 
     546             : #define MAP_NAME  ustlt_map
     547           0 : #define MAP_ELE_T ustlt_t
     548        1008 : #define MAP_IDX_T uint
     549             : #define MAP_KEY_T ustlt_key_t
     550           0 : #define MAP_NEXT  map_next
     551           0 : #define MAP_PREV  map_prev
     552           0 : #define MAP_KEY_HASH(k,s) fd_hash32( (k)->acct.b, (s)+((k)->block_idx) )
     553           0 : #define MAP_KEY_EQ(a,b)   (0==memcmp( (a), (b), sizeof(ustlt_key_t) ))
     554             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     555             : #include "../../util/tmpl/fd_map_chain.c"
     556             : 
     557             : #define DLIST_NAME  ustlt_dlist
     558             : #define DLIST_ELE_T ustlt_t
     559             : #define DLIST_IDX_T uint
     560   908630161 : #define DLIST_PREV  ll_prev
     561   908630161 : #define DLIST_NEXT  ll_next
     562             : #include "../../util/tmpl/fd_dlist.c"
     563             : 
     564             : 
     565             : 
     566             : /* fd_rdisp_blockinfo_t maintains a little metadata about transactions for each
     567             :    slot.  */
     568             : struct fd_rdisp_blockinfo {
     569             :   FD_RDISP_BLOCK_TAG_T block;
     570             :   uint  linear_block_number;
     571             : 
     572             :   uint  insert_ready:1;
     573             :   uint  schedule_ready:1;
     574             :   uint  staged:1;
     575             :   uint  staging_lane:2; /* ignored if staged==0 */
     576             :   uint  last_insert_was_serializing:1;
     577             :   uint  inserted_extra_ptxn:1;
     578             : 
     579             :   uint inserted_cnt;
     580             :   uint dispatched_cnt;
     581             :   uint completed_cnt;
     582             :   uint last_serializing;
     583             : 
     584             :   uint map_chain_next;
     585             :   uint ll_next;
     586             :   zombie_dlist_t    zombie_list[ 1 ];
     587             :   /* When unstaged: */
     588             :   unstaged_txn_ll_t ll[ 1 ];
     589             :   ustlt_dlist_t     ustlt_needed[ 1 ];
     590             :   ustlt_dlist_t     ustlt_created[ 1 ];
     591             : };
     592             : typedef struct fd_rdisp_blockinfo fd_rdisp_blockinfo_t;
     593             : 
     594             : #define POOL_NAME     block_pool
     595        3024 : #define POOL_T        fd_rdisp_blockinfo_t
     596             : #define POOL_IDX_T    uint
     597      131915 : #define POOL_NEXT     ll_next
     598             : #define POOL_SENTINEL 1
     599             : #include "../../util/tmpl/fd_pool.c"
     600             : 
     601             : #define MAP_NAME  block_map
     602             : #define MAP_ELE_T fd_rdisp_blockinfo_t
     603             : #define MAP_KEY_T FD_RDISP_BLOCK_TAG_T
     604        1001 : #define MAP_KEY   block
     605        1003 : #define MAP_NEXT  map_chain_next
     606        6096 : #define MAP_IDX_T uint
     607             : #include "../../util/tmpl/fd_map_chain.c"
     608             : 
     609             : #define SLIST_NAME  block_slist
     610             : #define SLIST_ELE_T fd_rdisp_blockinfo_t
     611           2 : #define SLIST_IDX_T uint
     612        1003 : #define SLIST_NEXT  ll_next
     613             : #include "../../util/tmpl/fd_slist.c"
     614             : 
     615             : struct fd_rdisp_unstaged {
     616             :   uint block_idx;
     617             : 
     618             :   uint writable_cnt;
     619             :   uint readonly_cnt;
     620             :   fd_acct_addr_t keys[FD_RDISP_MAX_ACCT_PER_TXN];
     621             : };
     622             : typedef struct fd_rdisp_unstaged fd_rdisp_unstaged_t;
     623             : 
     624             : 
     625             : 
     626             : typedef struct {
     627             :   pending_prq_ele_t * pending;
     628             :   ulong               linear_block_number;
     629             :   block_slist_t       block_ll[1];
     630             :   ulong               inserted_cnt;
     631             :   ulong               dispatched_cnt;
     632             :   ulong               completed_cnt;
     633             : } per_lane_info_t;
     634             : 
     635             : /* We maintain one map from pubkeys to acct_info_t, acct_map.  The
     636             :    ACTIVE pubkeys in this map have >0 references in one of the staging
     637             :    lane DAGs.  When an account goes to 0 references it stays in the map
     638             :    as CACHED, which exists to maintain the reference count EMA
     639             :    information lazily.  Unless we need the acct_info_t for something in
     640             :    the DAG, we might as well maintain the EMA info.
     641             : 
     642             :    When we start up, all the acct_info_t structs are in the
     643             :    free_acct_dlist.  Whenever something becomes CACHED, it's also added
     644             :    to the tail of the free_acct_dlist.  When we need an acct_info_t for
     645             :    a pubkey that's not in the map, we pop the head of the
     646             :    free_acct_dlist, evicting it from the map if it was CACHED.  In
     647             :    general, the free_acct_dlist contains everything CACHED, potentially
     648             :    plus some elements that have never been used; all acct_info_t objects
     649             :    are in at least one of acct_map and the free_acct_dlist.  See
     650             :    acct_info_t for more information about this. */
     651             : 
     652             : struct fd_rdisp {
     653             :   ulong depth;
     654             :   ulong block_depth;
     655             :   ulong acct_extra;
     656             : 
     657             :   ulong global_insert_cnt;
     658             :   ulong unstaged_lblk_num;
     659             : 
     660             :   /* pool: an fd_pool, indexed [0, depth+1), with 0 being a sentinel */
     661             :   fd_rdisp_txn_t       * pool;
     662             :   fd_rdisp_unstaged_t  * unstaged; /* parallel to pool with additional info */
     663             :   /* ptxn_pool: an fd_pool, also indexed [0, depth*128+1), with 0 as a
     664             :      sentinel. */
     665             :   fd_rdisp_ptxn_t      * ptxn_pool;
     666             :   fd_rdisp_mq_t        * mq;
     667             : 
     668             :   block_map_t          * blockmap; /* map chain */
     669             :   fd_rdisp_blockinfo_t * block_pool;
     670             : 
     671             :   float ready_acct_ema_max;
     672             : 
     673             :   int free_lanes; /* a bitmask */
     674             :   per_lane_info_t lanes[4];
     675             : 
     676             :   acct_map_t   * acct_map;
     677             :   /* acct_pool is not an fd_pool, but is just a flat array, since we
     678             :      don't need to acquire and release from it because of the dlist. */
     679             :   acct_info_t  * acct_pool;
     680             :   free_dlist_t   free_acct_dlist[1];
     681             : 
     682             :   /* Unfortunately, the LtHash info for unstaged transactions needs to
     683             :      be stored completely separately. */
     684             :   ustlt_t          * ustlt_pool; /* a flat array, not an fd_pool */
     685             :   ustlt_map_t      * ustlt_map;
     686             :   ustlt_dlist_t      ustlt_free[ 1 ];
     687             : };
     688             : 
     689             : typedef struct fd_rdisp fd_rdisp_t;
     690             : 
     691             : 
     692             :  #define ACCT_ITER_TO_PTR( iter ) (__extension__( {                                             \
     693             :        ulong __idx = fd_txn_acct_iter_idx( iter );                                              \
     694             :        fd_ptr_if( __idx<fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_IMM ), accts, alt_adj )+__idx; \
     695             :        }))
     696             : 
     697        6039 : #define MAX_WRITERS_PER_BLOCK FD_RDISP_MAX_WRITERS_PER_BLOCK
     698             : 
     699       34015 : ulong fd_rdisp_align( void ) { return 128UL; }
     700             : 
     701             : ulong
     702             : fd_rdisp_footprint( ulong depth,
     703             :                     ulong block_depth,
     704        3015 :                     ulong acct_extra ) {
     705        3015 :   if( FD_UNLIKELY( (depth>FD_RDISP_MAX_DEPTH)             | (depth<2UL) |
     706        3015 :                    (block_depth>FD_RDISP_MAX_BLOCK_DEPTH) | (block_depth<4UL) ) ) return 0UL;
     707        3015 :   ulong acct_depth     = depth*FD_RDISP_MAX_ACCT_PER_TXN+acct_extra;
     708        3015 :   if( FD_UNLIKELY( acct_depth>EDGE_SPECIAL_IDX_MAX ) ) return 0UL; /* account indices are 30-bit special edges */
     709             : 
     710        3015 :   ulong chain_cnt      = block_map_chain_cnt_est( block_depth );
     711             :   /* The map holds at most acct_depth entries, keep load factor at most
     712             :      0.5 to speed up chain operations. */
     713        3015 :   ulong acct_chain_cnt = acct_map_chain_cnt_est( 2UL*acct_depth );
     714        3015 :   ulong mq_footprint   = fd_rdisp_mq_footprint( acct_depth, MAX_WRITERS_PER_BLOCK );
     715        3015 :   ulong full_depth     = depth+acct_depth; /* normal and pseudo transactions */
     716             : 
     717        3015 :   ulong l = FD_LAYOUT_INIT;
     718        3015 :   l = FD_LAYOUT_APPEND( l, fd_rdisp_align(),             sizeof(fd_rdisp_t)                              );
     719        3015 :   l = FD_LAYOUT_APPEND( l, pool_align(),                 pool_footprint              ( depth+1UL       ) ); /* pool       */
     720        3015 :   l = FD_LAYOUT_APPEND( l, alignof(fd_rdisp_unstaged_t), sizeof(fd_rdisp_unstaged_t)*( depth+1UL       ) ); /* unstaged   */
     721        3015 :   l = FD_LAYOUT_APPEND( l, ptxn_pool_align(),            ptxn_pool_footprint         ( acct_depth+1UL  ) ); /* ptxn_pool  */
     722        3015 :   l = FD_LAYOUT_APPEND( l, fd_rdisp_mq_align(),          mq_footprint                                    ); /* mq         */
     723        3015 :   l = FD_LAYOUT_APPEND( l, block_map_align(),            block_map_footprint         ( chain_cnt       ) ); /* blockmap   */
     724        3015 :   l = FD_LAYOUT_APPEND( l, block_pool_align(),           block_pool_footprint        ( block_depth+1UL ) ); /* block_pool */
     725        3015 :   l = FD_LAYOUT_APPEND( l, pending_prq_align(),          4UL*pending_prq_footprint   ( full_depth      ) ); /* pending    */
     726        3015 :   l = FD_LAYOUT_APPEND( l, acct_map_align(),             acct_map_footprint          ( acct_chain_cnt  ) ); /* acct_map   */
     727        3015 :   l = FD_LAYOUT_APPEND( l, alignof(acct_info_t),         (acct_depth+1UL)*sizeof(acct_info_t)            ); /* acct_pool  */
     728        3015 :   l = FD_LAYOUT_APPEND( l, alignof(ustlt_t),             (acct_depth+1UL)*sizeof(ustlt_t)                ); /* ustlt_pool */
     729        3015 :   l = FD_LAYOUT_APPEND( l, ustlt_map_align(),            ustlt_map_footprint         ( acct_chain_cnt  ) ); /* ustlt_map  */
     730        3015 :   return FD_LAYOUT_FINI( l, fd_rdisp_align() );
     731        3015 : }
     732             : 
     733             : void *
     734             : fd_rdisp_new( void * mem,
     735             :               ulong  depth,
     736             :               ulong  block_depth,
     737             :               ulong  acct_extra,
     738        1008 :               ulong  seed ) {
     739        1008 :   if( FD_UNLIKELY( (depth>FD_RDISP_MAX_DEPTH)             | (depth<2UL) |
     740        1008 :                    (block_depth>FD_RDISP_MAX_BLOCK_DEPTH) | (block_depth<4UL) ) ) return NULL;
     741        1008 :   ulong acct_depth     = depth*FD_RDISP_MAX_ACCT_PER_TXN+acct_extra;
     742        1008 :   if( FD_UNLIKELY( acct_depth>EDGE_SPECIAL_IDX_MAX ) ) return NULL;
     743             : 
     744        1008 :   ulong chain_cnt      = block_map_chain_cnt_est( block_depth );
     745        1008 :   ulong acct_chain_cnt = acct_map_chain_cnt_est( 2UL*acct_depth ); /* see fd_rdisp_footprint */
     746        1008 :   ulong mq_footprint   = fd_rdisp_mq_footprint( acct_depth, MAX_WRITERS_PER_BLOCK );
     747        1008 :   ulong full_depth     = depth+acct_depth;
     748             : 
     749        1008 :   FD_SCRATCH_ALLOC_INIT( l, mem );
     750        1008 :   fd_rdisp_t * disp   = FD_SCRATCH_ALLOC_APPEND( l, fd_rdisp_align(),             sizeof(fd_rdisp_t)                              );
     751        1008 :   void  * _pool       = FD_SCRATCH_ALLOC_APPEND( l, pool_align(),                 pool_footprint              ( depth+1UL       ) );
     752        1008 :   void  * _unstaged   = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_rdisp_unstaged_t), sizeof(fd_rdisp_unstaged_t)*( depth+1UL       ) );
     753        1008 :   void  * _ptxn_pool  = FD_SCRATCH_ALLOC_APPEND( l, ptxn_pool_align(),            ptxn_pool_footprint         ( acct_depth+1UL  ) );
     754        1008 :   void  * _mq         = FD_SCRATCH_ALLOC_APPEND( l, fd_rdisp_mq_align(),          mq_footprint                                    );
     755        1008 :   void  * _bmap       = FD_SCRATCH_ALLOC_APPEND( l, block_map_align(),            block_map_footprint         ( chain_cnt       ) );
     756        1008 :   void  * _bpool      = FD_SCRATCH_ALLOC_APPEND( l, block_pool_align(),           block_pool_footprint        ( block_depth+1UL ) );
     757        1008 :   uchar * _pending    = FD_SCRATCH_ALLOC_APPEND( l, pending_prq_align(),          4UL*pending_prq_footprint   ( full_depth      ) );
     758        1008 :   void  * _acct_map   = FD_SCRATCH_ALLOC_APPEND( l, acct_map_align(),             acct_map_footprint          ( acct_chain_cnt  ) );
     759        1008 :   acct_info_t * apool = FD_SCRATCH_ALLOC_APPEND( l, alignof(acct_info_t),         (acct_depth+1UL)*sizeof(acct_info_t)            );
     760        1008 :   ustlt_t     * upool = FD_SCRATCH_ALLOC_APPEND( l, alignof(ustlt_t),             (acct_depth+1UL)*sizeof(ustlt_t)                );
     761        1008 :   void  * _ustlt_map  = FD_SCRATCH_ALLOC_APPEND( l, ustlt_map_align(),            ustlt_map_footprint         ( acct_chain_cnt  ) );
     762        1008 :   FD_SCRATCH_ALLOC_FINI( l, fd_rdisp_align() );
     763             : 
     764        1008 :   disp->depth             = depth;
     765        1008 :   disp->block_depth       = block_depth;
     766        1008 :   disp->acct_extra        = acct_extra;
     767        1008 :   disp->global_insert_cnt = 0UL;
     768        1008 :   disp->unstaged_lblk_num = 0UL;
     769             : 
     770        1008 :   fd_rdisp_txn_t * temp_pool_join = pool_join( pool_new( _pool, depth+1UL ) );
     771     3933125 :   for( ulong i=0UL; i<depth+1UL; i++ ) temp_pool_join[ i ].in_degree = IN_DEGREE_FREE;
     772        1008 :   pool_leave( temp_pool_join );
     773             : 
     774        1008 :   memset( _unstaged, '\0', sizeof(fd_rdisp_unstaged_t)*(depth+1UL) );
     775             : 
     776        1008 :   fd_rdisp_ptxn_t * temp_ppool_join = ptxn_pool_join( ptxn_pool_new( _ptxn_pool, acct_depth+1UL ) );
     777   989334745 :   for( ulong i=0UL; i<acct_depth+1UL; i++ ) temp_ppool_join[ i ].in_degree = IN_DEGREE_FREE;
     778        1008 :   ptxn_pool_leave( temp_ppool_join );
     779             : 
     780        1008 :   fd_rdisp_mq_new( _mq, acct_depth, MAX_WRITERS_PER_BLOCK );
     781             : 
     782        1008 :   block_map_new ( _bmap,  chain_cnt, seed );
     783        1008 :   block_pool_new( _bpool, block_depth+1UL );
     784             : 
     785        1008 :   fd_rdisp_blockinfo_t * bpool_temp_join = block_pool_join( _bpool );
     786      130461 :   for( ulong i=0UL; i<block_depth+1UL; i++ ) zombie_dlist_new( bpool_temp_join[ i ].zombie_list );
     787        1008 :   block_pool_leave( bpool_temp_join );
     788             : 
     789        1008 :   disp->ready_acct_ema_max = FD_RDISP_READY_ACCT_EMA_MAX;
     790             : 
     791        1008 :   disp->free_lanes = 0xF;
     792        5040 :   for( ulong i=0UL; i<4UL; i++ ) {
     793        4032 :     pending_prq_new( _pending, full_depth );
     794        4032 :     _pending += pending_prq_footprint( full_depth );
     795             : 
     796        4032 :     disp->lanes[i].linear_block_number = 0UL;
     797        4032 :     disp->lanes[i].inserted_cnt        = 0U;
     798        4032 :     disp->lanes[i].dispatched_cnt      = 0U;
     799        4032 :     disp->lanes[i].completed_cnt       = 0U;
     800        4032 :   }
     801             : 
     802        1008 :   acct_map_new( _acct_map,  acct_chain_cnt, fd_ulong_hash( seed+1UL ) );
     803             : 
     804        1008 :   free_dlist_t * temp_join = free_dlist_join( free_dlist_new( disp->free_acct_dlist ) );
     805   807338343 :   for( ulong i=1UL; i<acct_depth+1UL; i++ ) {
     806   807337335 :     apool[ i ].next = apool[ i ].prev = 0U;
     807   807337335 :     free_dlist_idx_push_tail( disp->free_acct_dlist, i, apool );
     808   807337335 :   }
     809        1008 :   free_dlist_leave( temp_join );
     810             : 
     811        1008 :   ustlt_map_new( _ustlt_map, acct_chain_cnt, fd_ulong_hash( seed+3UL ) );
     812             : 
     813        1008 :   ustlt_dlist_t * temp_join_ustlt = ustlt_dlist_join( ustlt_dlist_new( disp->ustlt_free ) );
     814   903644319 :   for( ulong i=1UL; i<acct_depth+1UL; i++ ) {
     815   903643311 :     upool[ i ].ll_next = upool[ i ].ll_prev = 0U;
     816   903643311 :     ustlt_dlist_idx_push_tail( disp->ustlt_free, i, upool );
     817   903643311 :   }
     818        1008 :   ustlt_dlist_leave( temp_join_ustlt );
     819             : 
     820             : 
     821        1008 :   return disp;
     822        1008 : }
     823             : 
     824             : fd_rdisp_t *
     825        1008 : fd_rdisp_join( void * mem ) {
     826        1008 :   fd_rdisp_t * disp = (fd_rdisp_t *)mem;
     827             : 
     828        1008 :   ulong depth          = disp->depth;
     829        1008 :   ulong block_depth    = disp->block_depth;
     830        1008 :   ulong chain_cnt      = block_map_chain_cnt_est( block_depth );
     831        1008 :   ulong acct_depth     = depth*FD_RDISP_MAX_ACCT_PER_TXN+disp->acct_extra;
     832        1008 :   ulong acct_chain_cnt = acct_map_chain_cnt_est( 2UL*acct_depth ); /* see fd_rdisp_footprint */
     833        1008 :   ulong mq_footprint   = fd_rdisp_mq_footprint( acct_depth, MAX_WRITERS_PER_BLOCK );
     834        1008 :   ulong full_depth     = depth+acct_depth;
     835             : 
     836        1008 :   FD_SCRATCH_ALLOC_INIT( l, mem );
     837        1008 :   /*                 */ FD_SCRATCH_ALLOC_APPEND( l, fd_rdisp_align(),             sizeof(fd_rdisp_t)                              );
     838        1008 :   void  * _pool       = FD_SCRATCH_ALLOC_APPEND( l, pool_align(),                 pool_footprint              ( depth+1UL       ) );
     839        1008 :   void  * _unstaged   = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_rdisp_unstaged_t), sizeof(fd_rdisp_unstaged_t)*( depth+1UL       ) );
     840        1008 :   void  * _ptxn_pool  = FD_SCRATCH_ALLOC_APPEND( l, ptxn_pool_align(),            ptxn_pool_footprint         ( acct_depth+1UL  ) );
     841        1008 :   void  * _mq         = FD_SCRATCH_ALLOC_APPEND( l, fd_rdisp_mq_align(),          mq_footprint                                    );
     842        1008 :   void  * _bmap       = FD_SCRATCH_ALLOC_APPEND( l, block_map_align(),            block_map_footprint         ( chain_cnt       ) );
     843        1008 :   void  * _bpool      = FD_SCRATCH_ALLOC_APPEND( l, block_pool_align(),           block_pool_footprint        ( block_depth+1UL ) );
     844        1008 :   uchar * _pending    = FD_SCRATCH_ALLOC_APPEND( l, pending_prq_align(),          4UL*pending_prq_footprint   ( full_depth      ) );
     845        1008 :   void  * _acct_map   = FD_SCRATCH_ALLOC_APPEND( l, acct_map_align(),             acct_map_footprint          ( acct_chain_cnt  ) );
     846        1008 :   acct_info_t * apool = FD_SCRATCH_ALLOC_APPEND( l, alignof(acct_info_t),         (acct_depth+1UL)*sizeof(acct_info_t)            );
     847        1008 :   ustlt_t     * upool = FD_SCRATCH_ALLOC_APPEND( l, alignof(ustlt_t),             (acct_depth+1UL)*sizeof(ustlt_t)                );
     848        1008 :   void  * _ustlt_map  = FD_SCRATCH_ALLOC_APPEND( l, ustlt_map_align(),            ustlt_map_footprint         ( acct_chain_cnt  ) );
     849        1008 :   FD_SCRATCH_ALLOC_FINI( l, fd_rdisp_align() );
     850             : 
     851        1008 :   disp->pool       = pool_join( _pool );
     852        1008 :   disp->unstaged   = (fd_rdisp_unstaged_t *)_unstaged;
     853        1008 :   disp->ptxn_pool  = ptxn_pool_join( _ptxn_pool );
     854        1008 :   disp->mq         = fd_rdisp_mq_join( _mq );
     855        1008 :   disp->blockmap   = block_map_join( _bmap );
     856        1008 :   disp->block_pool = block_pool_join( _bpool );
     857             : 
     858      131040 :   for( ulong i=0UL; i<block_depth+1UL; i++ ) zombie_dlist_join( disp->block_pool[ i ].zombie_list );
     859             : 
     860        5040 :   for( ulong i=0UL; i<4UL; i++ ) {
     861        4032 :     disp->lanes[i].pending = pending_prq_join( _pending );
     862        4032 :     _pending += pending_prq_footprint( full_depth );
     863        4032 :   }
     864             : 
     865        1008 :   disp->acct_map      = acct_map_join( _acct_map );
     866        1008 :   disp->acct_pool     = apool;
     867        1008 :   free_dlist_join( disp->free_acct_dlist );
     868             : 
     869        1008 :   disp->ustlt_pool    = upool;
     870        1008 :   disp->ustlt_map     = ustlt_map_join( _ustlt_map );
     871        1008 :   ustlt_dlist_join( disp->ustlt_free );
     872             : 
     873        1008 :   return disp;
     874        1008 : }
     875             : 
     876             : static inline void
     877             : free_lane( fd_rdisp_t * disp,
     878           2 :            ulong        staging_lane ) {
     879           2 :   disp->free_lanes |= 1<<staging_lane;
     880           2 :   per_lane_info_t * l = disp->lanes+staging_lane;
     881           2 :   FD_TEST( pending_prq_cnt( l->pending )==0UL );
     882           2 :   l->linear_block_number = 0UL;
     883           2 :   l->inserted_cnt   = 0UL;
     884           2 :   l->dispatched_cnt = 0UL;
     885           2 :   l->completed_cnt  = 0UL;
     886           2 :   block_slist_delete( block_slist_leave( l->block_ll ) );
     887           2 : }
     888             : 
     889             : static inline void
     890             : alloc_lane( fd_rdisp_t * disp,
     891        1001 :             ulong        staging_lane ) {
     892        1001 :   disp->free_lanes &= ~(1<<staging_lane);
     893        1001 :   block_slist_join( block_slist_new( disp->lanes[staging_lane].block_ll ) );
     894        1001 : }
     895             : 
     896             : ulong
     897             : fd_rdisp_suggest_staging_lane( fd_rdisp_t const *   disp,
     898             :                                FD_RDISP_BLOCK_TAG_T parent_block,
     899           0 :                                int                  duplicate ) {
     900             : 
     901             :   /* 1. If it's a duplicate, suggest FD_RDISP_UNSTAGED */
     902           0 :   if( FD_UNLIKELY( duplicate ) ) return FD_RDISP_UNSTAGED;
     903             : 
     904             :   /* 2. If parent is the last block in any existing staging lane, suggest
     905             :         that lane */
     906           0 :   fd_rdisp_blockinfo_t const * block_pool = disp->block_pool;
     907           0 :   fd_rdisp_blockinfo_t const * block = block_map_ele_query_const( disp->blockmap, &parent_block, NULL, block_pool );
     908           0 :   if( FD_LIKELY( block && block->insert_ready && block->staged ) ) return block->staging_lane;
     909             : 
     910             :   /* 3. If there is at least one free lane, suggest a free lane */
     911           0 :   if( FD_LIKELY( disp->free_lanes!=0 ) ) return (ulong)fd_uint_find_lsb( (uint)disp->free_lanes );
     912             : 
     913             :   /* 4. Else, suggest FD_RDISP_UNSTAGED */
     914           0 :   return FD_RDISP_UNSTAGED;
     915           0 : }
     916             : 
     917             : int
     918             : fd_rdisp_add_block( fd_rdisp_t          * disp,
     919             :                    FD_RDISP_BLOCK_TAG_T   new_block,
     920        1001 :                    ulong                  staging_lane ) {
     921        1001 :   fd_rdisp_blockinfo_t * block_pool = disp->block_pool;
     922             : 
     923        1001 :   if( FD_UNLIKELY( !block_pool_free( block_pool )                                                             ) ) return -1;
     924        1001 :   if( FD_UNLIKELY(  ULONG_MAX!=block_map_idx_query_const( disp->blockmap, &new_block, ULONG_MAX, block_pool ) ) ) return -1;
     925        1001 :   fd_rdisp_blockinfo_t * block = block_pool_ele_acquire( block_pool );
     926        1001 :   block->block = new_block;
     927        1001 :   block_map_ele_insert( disp->blockmap, block, block_pool );
     928             : 
     929        1001 :   block->insert_ready = 1;
     930        1001 :   block->staged       = staging_lane!=FD_RDISP_UNSTAGED;
     931        1001 :   block->staging_lane = (uint)(staging_lane & 0x3UL);
     932        1001 :   block->last_insert_was_serializing = 0U;
     933        1001 :   block->inserted_extra_ptxn         = 0U;
     934             : 
     935        1001 :   block->inserted_cnt     = 0U;
     936        1001 :   block->dispatched_cnt   = 0U;
     937        1001 :   block->completed_cnt    = 0U;
     938        1001 :   block->last_serializing = 0U;
     939             : 
     940        1001 :   if( FD_UNLIKELY( staging_lane==FD_RDISP_UNSTAGED ) ) {
     941           0 :     block->schedule_ready = 1;
     942           0 :     block->linear_block_number = (uint)disp->unstaged_lblk_num++;
     943           0 :     unstaged_txn_ll_join( unstaged_txn_ll_new( block->ll            ) );
     944           0 :     ustlt_dlist_join(     ustlt_dlist_new    ( block->ustlt_needed  ) );
     945           0 :     ustlt_dlist_join(     ustlt_dlist_new    ( block->ustlt_created ) );
     946        1001 :   } else {
     947        1001 :     block->linear_block_number = (uint)disp->lanes[staging_lane].linear_block_number++;
     948        1001 :     block->schedule_ready      = (uint)(1 & (disp->free_lanes >> staging_lane));
     949             : 
     950        1001 :     if( FD_LIKELY( disp->free_lanes & (1<<staging_lane) ) ) alloc_lane( disp, staging_lane );
     951             : 
     952        1001 :     block_slist_t * sl = disp->lanes[staging_lane].block_ll;
     953        1001 :     if( FD_LIKELY( !block_slist_is_empty( sl, block_pool ) ) )  block_slist_ele_peek_tail( sl, block_pool )->insert_ready = 0;
     954        1001 :     block_slist_ele_push_tail( sl, block, block_pool );
     955        1001 :   }
     956        1001 :   return 0;
     957        1001 : }
     958             : 
     959             : 
     960             : 
     961             : int
     962             : fd_rdisp_remove_block( fd_rdisp_t          * disp,
     963           0 :                        FD_RDISP_BLOCK_TAG_T  block_tag ) {
     964           0 :   fd_rdisp_blockinfo_t * block_pool = disp->block_pool;
     965             : 
     966           0 :   fd_rdisp_blockinfo_t * block   = block_map_ele_query( disp->blockmap, &block_tag, NULL, block_pool );
     967           0 :   if( FD_UNLIKELY( block==NULL ) ) return -1;
     968             : 
     969           0 :   FD_TEST( block->schedule_ready );
     970           0 :   FD_TEST( block->completed_cnt==block->inserted_cnt );
     971           0 :   FD_TEST( zombie_dlist_is_empty( block->zombie_list, disp->pool ) );
     972             : 
     973           0 :   if( FD_LIKELY( block->staged ) ) {
     974           0 :     ulong staging_lane = (ulong)block->staging_lane;
     975           0 :     block_slist_t * sl = disp->lanes[staging_lane].block_ll;
     976             : 
     977           0 :     FD_TEST( block==block_slist_ele_peek_head( sl, block_pool ) );
     978           0 :     block_slist_idx_pop_head( sl, block_pool );
     979           0 :     if( FD_LIKELY( !block_slist_is_empty( sl, block_pool ) ) ) block_slist_ele_peek_head( sl, block_pool )->schedule_ready = 1;
     980           0 :     else                                                       free_lane( disp, staging_lane );
     981           0 :   } else {
     982           0 :     FD_TEST( ustlt_dlist_is_empty( block->ustlt_needed, disp->ustlt_pool ) && ustlt_dlist_is_empty( block->ustlt_created, disp->ustlt_pool ) );
     983           0 :     unstaged_txn_ll_delete( unstaged_txn_ll_leave( block->ll            ) );
     984           0 :     ustlt_dlist_delete    ( ustlt_dlist_leave    ( block->ustlt_needed  ) );
     985           0 :     ustlt_dlist_delete    ( ustlt_dlist_leave    ( block->ustlt_created ) );
     986           0 :   }
     987           0 :   block_pool_idx_release( block_pool, block_map_idx_remove( disp->blockmap, &block_tag, ULONG_MAX, block_pool ) );
     988             : 
     989           0 :   return 0;
     990           0 : }
     991             : 
     992             : 
     993             : int
     994             : fd_rdisp_abandon_block( fd_rdisp_t          * disp,
     995           2 :                         FD_RDISP_BLOCK_TAG_T  block_tag ) {
     996           2 :   fd_rdisp_blockinfo_t * block_pool = disp->block_pool;
     997             : 
     998           2 :   fd_rdisp_blockinfo_t * block   = block_map_ele_query( disp->blockmap, &block_tag, NULL, disp->block_pool );
     999           2 :   if( FD_UNLIKELY( block==NULL ) ) return -1;
    1000             : 
    1001           2 :   FD_TEST( block->schedule_ready );
    1002           2 :   FD_TEST( block->dispatched_cnt==block->completed_cnt ); /* TODO: remove this when it can call complete properly */
    1003           2 :   while( block->completed_cnt<block->inserted_cnt ) {
    1004             :     /* because there is nothing DISPATCHED, there has to be something
    1005             :        READY */
    1006           0 :     ulong txn = fd_rdisp_get_next_ready( disp, block_tag );
    1007           0 :     FD_TEST( txn );
    1008           0 :     fd_rdisp_complete_txn( disp, txn, 1 );
    1009           0 :   }
    1010           2 :   while( !zombie_dlist_is_empty( block->zombie_list, disp->pool ) ) {
    1011             :     /* rdisp_complete_txn removes the peeked head */
    1012           0 :     fd_rdisp_complete_txn( disp, zombie_dlist_idx_peek_head( block->zombie_list, disp->pool ), 1 );
    1013           0 :   }
    1014             : 
    1015           2 :   if( FD_LIKELY( block->staged ) ) {
    1016           2 :     ulong staging_lane = (ulong)block->staging_lane;
    1017             : 
    1018           2 :     block_slist_t * sl = disp->lanes[staging_lane].block_ll;
    1019             : 
    1020           2 :     FD_TEST( block==block_slist_ele_peek_head( sl, block_pool ) );
    1021           2 :     block_slist_idx_pop_head( sl, block_pool );
    1022           2 :     if( FD_LIKELY( !block_slist_is_empty( sl, block_pool ) ) ) block_slist_ele_peek_head( sl, block_pool )->schedule_ready = 1;
    1023           2 :     else {
    1024             :       /* It's schedule-ready, so it's possible there are some accounts
    1025             :          in the NEEDS_PTXN state (add writer, schedule, complete it).
    1026             :          However, if it's not insert-ready, then we know the caller
    1027             :          would already have created the pseudo-transactions if they were
    1028             :          needed, so we don't need to do anything in that case. */
    1029           2 :       while( fd_rdisp_mq_cnt( disp->mq, staging_lane ) ) {
    1030           0 :         ulong acct_idx = fd_rdisp_mq_pop( disp->mq, staging_lane );
    1031           0 :         acct_info_t * ai = disp->acct_pool + acct_idx;
    1032           0 :         uint s = ai->states;
    1033           0 :         if( FD_UNLIKELY( ACCT_INFO_STATE_IS(s, staging_lane, NEEDS_PTXN) ) ) {
    1034           0 :           ai->states = (uchar)ACCT_INFO_STATE_SET(s, staging_lane, NO_WRITERS);
    1035             : 
    1036             :           /* Potentially transition from ACTIVE -> CACHED */
    1037           0 :           if( FD_UNLIKELY( (ai->states==0)&
    1038           0 :                 (ai->last_reference[ 0 ]==0U)&(ai->last_reference[ 1 ]==0U)&
    1039           0 :                 (ai->last_reference[ 2 ]==0U)&(ai->last_reference[ 3 ]==0U) ) ) {
    1040           0 :             ai->cached = ACCT_INFO_CACHED;
    1041           0 :             free_dlist_idx_push_tail( disp->free_acct_dlist, acct_idx, disp->acct_pool );
    1042           0 :           }
    1043           0 :         }
    1044           0 :       }
    1045             : 
    1046           2 :       free_lane( disp, staging_lane );
    1047           2 :     }
    1048           2 :   } else {
    1049           0 :     while( !ustlt_dlist_is_empty( block->ustlt_created, disp->ustlt_pool ) ) {
    1050           0 :       ustlt_t * ele = ustlt_dlist_ele_peek_head( block->ustlt_created, disp->ustlt_pool );
    1051             :       /* Free the created ones */
    1052           0 :       fd_rdisp_ptxn_t * ptxn = disp->ptxn_pool + EDGE_SPECIAL_IDX( ele->ptxn_idx );
    1053           0 :       ptxn->in_degree        = IN_DEGREE_FREE;
    1054           0 :       ptxn_pool_ele_release( disp->ptxn_pool, ptxn );
    1055             : 
    1056           0 :       ustlt_dlist_ele_remove   ( block->ustlt_created, ele, disp->ustlt_pool );
    1057           0 :       ustlt_dlist_ele_push_tail( disp->ustlt_free,     ele, disp->ustlt_pool );
    1058           0 :     }
    1059             : 
    1060           0 :     while( !ustlt_dlist_is_empty( block->ustlt_needed, disp->ustlt_pool ) ) {
    1061           0 :       ustlt_t * ele = ustlt_dlist_ele_peek_head( block->ustlt_needed, disp->ustlt_pool );
    1062           0 :       ustlt_dlist_ele_remove   ( block->ustlt_needed, ele, disp->ustlt_pool );
    1063           0 :       ustlt_map_ele_remove_fast( disp->ustlt_map,     ele, disp->ustlt_pool );
    1064           0 :       ustlt_dlist_ele_push_tail( disp->ustlt_free,    ele, disp->ustlt_pool );
    1065           0 :     }
    1066             : 
    1067           0 :     unstaged_txn_ll_delete( unstaged_txn_ll_leave( block->ll ) );
    1068           0 :     ustlt_dlist_delete    ( ustlt_dlist_leave    ( block->ustlt_needed  ) );
    1069           0 :     ustlt_dlist_delete    ( ustlt_dlist_leave    ( block->ustlt_created ) );
    1070           0 :   }
    1071           2 :   block_pool_idx_release( disp->block_pool, block_map_idx_remove( disp->blockmap, &block_tag, ULONG_MAX, disp->block_pool ) );
    1072             : 
    1073           2 :   return 0;
    1074           2 : }
    1075             : 
    1076             : static void
    1077             : add_edges( fd_rdisp_t           * disp,
    1078             :            fd_rdisp_txn_t       * ele,
    1079             :            fd_acct_addr_t const * addr,
    1080             :            ulong                  addr_cnt,
    1081             :            uint                   staging_lane,
    1082             :            int                    writable,
    1083             :            int                    update_score,
    1084             :            fd_rdisp_neww_t *      new_wr,
    1085             :            ulong                  new_wr_offset );
    1086             : /* updates in_degree, edge_cnt_etc */
    1087             : 
    1088             : 
    1089             : static inline ulong
    1090             : lookup_pubkey( fd_rdisp_t * disp,
    1091             :                fd_acct_addr_t const * addr );
    1092             : 
    1093             : static void
    1094             : untrack_writer( fd_rdisp_t * disp,
    1095             :                 ulong        staging_lane,
    1096           0 :                 ulong        acct_idx ) {
    1097           0 :   acct_info_t * ai = disp->acct_pool + acct_idx;
    1098           0 :   uint s = ai->states;
    1099           0 :   if( FD_UNLIKELY( ACCT_INFO_STATE_IS(s, staging_lane, NEEDS_PTXN) ) ) {
    1100           0 :     ai->states = (uchar)ACCT_INFO_STATE_SET(s, staging_lane, NO_WRITERS);
    1101             : 
    1102             :     /* Potentially transition from ACTIVE -> CACHED */
    1103           0 :     if( FD_UNLIKELY( (ai->states==0)&
    1104           0 :           (ai->last_reference[ 0 ]==0U)&(ai->last_reference[ 1 ]==0U)&
    1105           0 :           (ai->last_reference[ 2 ]==0U)&(ai->last_reference[ 3 ]==0U) ) ) {
    1106           0 :       ai->cached = ACCT_INFO_CACHED;
    1107           0 :       free_dlist_idx_push_tail( disp->free_acct_dlist, acct_idx, disp->acct_pool );
    1108           0 :     }
    1109           0 :   }
    1110           0 : }
    1111             : 
    1112             : /* Priority of an account in a lane's writable set.  An account whose
    1113             :    every writer in the block has completed, so that an addition
    1114             :    pseudo-transaction for it would be READY on creation, is ready and
    1115             :    sits at priority 0 if its reference EMA is under the threshold.
    1116             :    Every other account waits at 1 plus its EMA, so the coldest comes out
    1117             :    first.  An account over the threshold is considered hot enough to
    1118             :    likely be written again before the block ends, so a speculative
    1119             :    addition on it would likely have to be undone. */
    1120             : static inline float
    1121             : mq_prio( fd_rdisp_t const *  disp,
    1122             :          acct_info_t const * ai,
    1123        2889 :          int                 ready ) {
    1124        2889 :   return fd_float_if( ready && ai->ema_refs<disp->ready_acct_ema_max, 0.0f, 1.0f+ai->ema_refs );
    1125        2889 : }
    1126             : 
    1127             : int
    1128             : fd_rdisp_promote_block( fd_rdisp_t *          disp,
    1129             :                         FD_RDISP_BLOCK_TAG_T  block_tag,
    1130           0 :                         ulong                 staging_lane ) {
    1131           0 :   fd_rdisp_blockinfo_t * block_pool = disp->block_pool;
    1132           0 :   per_lane_info_t * lane = disp->lanes + staging_lane;
    1133           0 :   block_slist_t * sl = lane->block_ll;
    1134             : 
    1135           0 :   fd_rdisp_blockinfo_t * block   = block_map_ele_query( disp->blockmap, &block_tag, NULL, block_pool );
    1136           0 :   if( FD_UNLIKELY( block==NULL   ) ) return -1;
    1137           0 :   if( FD_UNLIKELY( block->staged ) ) return -1;
    1138             : 
    1139           0 :   block->staged = 1;
    1140           0 :   block->staging_lane = (uint)(staging_lane & 0x3);
    1141           0 :   block->insert_ready = 1;
    1142           0 :   block->schedule_ready = (uint)(1 & (disp->free_lanes >> staging_lane));
    1143             : 
    1144           0 :   if( FD_LIKELY( disp->free_lanes & (1<<staging_lane) ) ) alloc_lane( disp, staging_lane );
    1145             : 
    1146           0 :   if( FD_LIKELY( !block_slist_is_empty( sl, block_pool ) ) )  block_slist_ele_peek_tail( sl, block_pool )->insert_ready = 0;
    1147           0 :   block_slist_ele_push_tail( sl, block, block_pool );
    1148           0 :   uint linear_block_number = (uint)lane->linear_block_number++;
    1149           0 :   block->linear_block_number = linear_block_number;
    1150             : 
    1151           0 :   unstaged_txn_ll_iter_t next;
    1152           0 :   for( unstaged_txn_ll_iter_t iter = unstaged_txn_ll_iter_init( block->ll, disp->pool );
    1153           0 :       !unstaged_txn_ll_iter_done( iter, block->ll, disp->pool );
    1154           0 :       iter = next ) {
    1155           0 :       next = unstaged_txn_ll_iter_next( iter, block->ll, disp->pool );
    1156             : 
    1157           0 :     fd_rdisp_txn_t      * ele = unstaged_txn_ll_iter_ele( iter, block->ll, disp->pool );
    1158           0 :     fd_rdisp_unstaged_t * uns = disp->unstaged + unstaged_txn_ll_iter_idx( iter, block->ll, disp->pool );
    1159           0 :     FD_TEST( ele->in_degree==IN_DEGREE_UNSTAGED );
    1160             : 
    1161           0 :     ele->in_degree    = 0U;
    1162           0 :     ele->edge_cnt_etc = (uint)(-1); /* set w_cnt_1 to -1 */
    1163             : 
    1164             :     /* Ignore LtHash accounts here since we have the precise list in
    1165             :        ustlt_needed. */
    1166           0 :     add_edges( disp, ele, uns->keys,                   uns->writable_cnt, (uint)staging_lane, 1, 0, NULL, 0UL );
    1167           0 :     add_edges( disp, ele, uns->keys+uns->writable_cnt, uns->readonly_cnt, (uint)staging_lane, 0, 0, NULL, 0UL );
    1168             : 
    1169           0 :     ele->edge_cnt_etc &= 0x3FFFU;
    1170           0 :     ele->edge_cnt_etc |= (uint)staging_lane<<14;
    1171           0 :     ele->edge_cnt_etc |= linear_block_number<<16;
    1172             : 
    1173           0 :     if( FD_UNLIKELY( ele->in_degree==0U ) ) {
    1174           0 :       pending_prq_ele_t temp[1] = {{ .score = ele->score, .linear_block_number = linear_block_number, .txn_idx = (uint)(ele-disp->pool)}};
    1175           0 :       pending_prq_insert( lane->pending, temp );
    1176           0 :     }
    1177           0 :   }
    1178           0 :   unstaged_txn_ll_delete( unstaged_txn_ll_leave( block->ll ) );
    1179             : 
    1180             :   /* Any pseudo-transactions created while the block was unstaged must
    1181             :      have completed by now.  Freeing them here would invalidate indices
    1182             :      the caller still holds. */
    1183           0 :   FD_TEST( ustlt_dlist_is_empty( block->ustlt_created, disp->ustlt_pool ) );
    1184             : 
    1185           0 :   while( !ustlt_dlist_is_empty( block->ustlt_needed, disp->ustlt_pool ) ) {
    1186           0 :     ustlt_t * ele = ustlt_dlist_ele_peek_head( block->ustlt_needed, disp->ustlt_pool );
    1187             : 
    1188           0 :     ulong acct_idx = lookup_pubkey( disp, &(ele->key.acct) );
    1189           0 :     acct_info_t * ai = disp->acct_pool + acct_idx;
    1190             :     /* If it's NO_WRITERS, we need to transition to NEEDS_PTXN, but
    1191             :        otherwise it's fine. */
    1192           0 :     uint s = ai->states;
    1193           0 :     s = fd_uint_if( ACCT_INFO_STATE_IS(s, staging_lane, NO_WRITERS), ACCT_INFO_STATE_SET(s, staging_lane, NEEDS_PTXN), s );
    1194           0 :     ai->states = (uchar)s;
    1195             : 
    1196           0 :     if( FD_UNLIKELY( fd_rdisp_mq_cnt( disp->mq, staging_lane )>=MAX_WRITERS_PER_BLOCK ) ) FD_LOG_CRIT(( "rdisp lane %lu writable account set full", staging_lane ));
    1197           0 :     fd_rdisp_mq_insert( disp->mq, acct_idx, staging_lane, mq_prio( disp, ai, ACCT_INFO_STATE_IS( s, staging_lane, NEEDS_PTXN ) ) );
    1198             : 
    1199           0 :     ustlt_dlist_ele_remove   ( block->ustlt_needed, ele, disp->ustlt_pool );
    1200           0 :     ustlt_map_ele_remove_fast( disp->ustlt_map,     ele, disp->ustlt_pool );
    1201           0 :     ustlt_dlist_ele_push_tail( disp->ustlt_free,    ele, disp->ustlt_pool );
    1202           0 :   }
    1203             : 
    1204           0 :   lane->inserted_cnt   += block->inserted_cnt;
    1205           0 :   lane->dispatched_cnt += block->dispatched_cnt;
    1206           0 :   lane->completed_cnt  += block->completed_cnt;
    1207             : 
    1208           0 :   return 0;
    1209           0 : }
    1210             : 
    1211             : int
    1212             : fd_rdisp_demote_block( fd_rdisp_t *          disp,
    1213           0 :                        FD_RDISP_BLOCK_TAG_T  block_tag ) {
    1214           0 :   fd_rdisp_blockinfo_t * block_pool = disp->block_pool;
    1215             : 
    1216           0 :   fd_rdisp_blockinfo_t * block   = block_map_ele_query( disp->blockmap, &block_tag, NULL, block_pool );
    1217           0 :   if( FD_UNLIKELY(  block==NULL           ) ) return -1;
    1218           0 :   if( FD_UNLIKELY( !block->staged         ) ) return -1;
    1219           0 :   if( FD_UNLIKELY( !block->schedule_ready ) ) return -1;
    1220           0 :   if( FD_UNLIKELY(  block->completed_cnt!=block->inserted_cnt ) ) FD_LOG_ERR(( "demote_block called with non-empty block" ));
    1221           0 :   ulong staging_lane = block->staging_lane;
    1222           0 :   block->staged = 0;
    1223             : 
    1224           0 :   per_lane_info_t * lane = disp->lanes + staging_lane;
    1225           0 :   block_slist_t * sl = lane->block_ll;
    1226             : 
    1227           0 :   lane->inserted_cnt   -= block->inserted_cnt;
    1228           0 :   lane->dispatched_cnt -= block->dispatched_cnt;
    1229           0 :   lane->completed_cnt  -= block->completed_cnt;
    1230             : 
    1231           0 :   block->linear_block_number = (uint)disp->unstaged_lblk_num++;
    1232             : 
    1233             :   /* staged and schedule_ready means it must be the head of the staging lane */
    1234           0 :   FD_TEST( block_slist_ele_peek_head( sl, block_pool )==block );
    1235           0 :   block_slist_idx_pop_head( sl, block_pool );
    1236             : 
    1237           0 :   unstaged_txn_ll_join( unstaged_txn_ll_new( block->ll            ) );
    1238           0 :   ustlt_dlist_join(     ustlt_dlist_new    ( block->ustlt_needed  ) );
    1239           0 :   ustlt_dlist_join(     ustlt_dlist_new    ( block->ustlt_created ) );
    1240             : 
    1241             : 
    1242           0 :   if( FD_LIKELY( !block_slist_is_empty( sl, block_pool ) ) ) block_slist_ele_peek_head( sl, block_pool )->schedule_ready = 1;
    1243           0 :   else {
    1244             :     /* If it was insert-ready, we need to carry over the LtHash addition
    1245             :        information.  If it wasn't, then that information should have
    1246             :        been encoded in the created LtHash addition tasks, but the block
    1247             :        can't contain any PENDING transactions by contract... */
    1248           0 :     while( fd_rdisp_mq_cnt( disp->mq, staging_lane ) ) {
    1249           0 :       ulong acct_idx = fd_rdisp_mq_pop( disp->mq, staging_lane );
    1250           0 :       acct_info_t * ai = disp->acct_pool + acct_idx;
    1251             : 
    1252           0 :       if( FD_UNLIKELY( ustlt_dlist_is_empty( disp->ustlt_free, disp->ustlt_pool ) ) ) FD_LOG_CRIT(( "rdisp unstaged LtHash entry pool exhausted" ));
    1253           0 :       ulong ustlt_idx = ustlt_dlist_idx_pop_head( disp->ustlt_free, disp->ustlt_pool );
    1254           0 :       disp->ustlt_pool[ ustlt_idx ].key.acct      = ai->key;
    1255           0 :       disp->ustlt_pool[ ustlt_idx ].key.block_idx = (uint)(block-block_pool);
    1256           0 :       ustlt_map_idx_insert( disp->ustlt_map, ustlt_idx, disp->ustlt_pool );
    1257           0 :       ustlt_dlist_idx_push_tail( block->ustlt_needed, ustlt_idx, disp->ustlt_pool );
    1258             : 
    1259           0 :       untrack_writer( disp, staging_lane, acct_idx );
    1260           0 :     }
    1261           0 :     free_lane( disp, staging_lane );
    1262           0 :   }
    1263           0 :   return 0;
    1264           0 : }
    1265             : 
    1266             : int
    1267             : fd_rdisp_clear_writable_set( fd_rdisp_t *          disp,
    1268           0 :                              FD_RDISP_BLOCK_TAG_T  block_tag ) {
    1269           0 :   fd_rdisp_blockinfo_t * block = block_map_ele_query( disp->blockmap, &block_tag, NULL, disp->block_pool );
    1270           0 :   if( FD_UNLIKELY(  block==NULL           ) ) return -1;
    1271           0 :   if( FD_UNLIKELY( !block->staged         ) ) return -1;
    1272           0 :   if( FD_UNLIKELY( !block->schedule_ready ) ) return -1;
    1273           0 :   ulong staging_lane = block->staging_lane;
    1274           0 :   while( fd_rdisp_mq_cnt( disp->mq, staging_lane ) ) untrack_writer( disp, staging_lane, fd_rdisp_mq_pop( disp->mq, staging_lane ) );
    1275           0 :   return 0;
    1276           0 : }
    1277             : 
    1278             : int
    1279             : fd_rdisp_rekey_block( fd_rdisp_t *           disp,
    1280             :                       FD_RDISP_BLOCK_TAG_T   new_tag,
    1281           0 :                       FD_RDISP_BLOCK_TAG_T   old_tag ) {
    1282           0 :   fd_rdisp_blockinfo_t * block_pool = disp->block_pool;
    1283             : 
    1284           0 :   if( FD_UNLIKELY(        NULL!= block_map_ele_query_const( disp->blockmap, &new_tag, NULL, block_pool ) ) ) return -1;
    1285           0 :   fd_rdisp_blockinfo_t * block = block_map_ele_query      ( disp->blockmap, &old_tag, NULL, block_pool );
    1286           0 :   if( FD_UNLIKELY(        NULL== block ) )                                                                   return -1;
    1287             : 
    1288           0 :   block_map_idx_remove( disp->blockmap, &old_tag, ULONG_MAX, block_pool );
    1289           0 :   block->block = new_tag;
    1290           0 :   block_map_ele_insert( disp->blockmap, block, block_pool );
    1291           0 :   return 0;
    1292           0 : }
    1293             : 
    1294             : 
    1295             : /* "Registers" a reference to the account in info at transaction
    1296             :    global_insert_cnt.  Returns the value of the EMA, which is an
    1297             :    estimate of the probability that the next transaction also references
    1298             :    the account.  This value does not matter for correctness, other than
    1299             :    that it is in [0, 1), which is why floating point arithmetic is
    1300             :    acceptable here. */
    1301             : static inline float
    1302             : update_ema( acct_info_t * info,
    1303        4688 :             ulong         global_insert_cnt ) {
    1304             : #if FD_RDISP_DISABLE_EMA
    1305             :   (void)info;
    1306             :   (void)global_insert_cnt;
    1307             :   return 0.0f;
    1308             : #else
    1309        9376 : #define ALPHA 0.005f
    1310             :   /* The normal EMA update equation is
    1311             :                 e_i = (alpha) * x_i + (1-alpha)*e_{i-1},
    1312             :      where alpha is a constant in (0,1).  Let L be the last reference of
    1313             :      the account, and G be the current global_insert_cnt value.  We know
    1314             :      that e_L = ema_refs, and that for i in [L+1, G), x_i = 0.
    1315             :      That means
    1316             :                e_{G-1} =         (1-alpha)^(G-L-1) * ema_refs
    1317             :                e_G     = alpha + (1-alpha)^(G-L)   * ema_refs
    1318             :    */
    1319             :   /* last_ref only captures the low 24 bits, so we guess its the highest
    1320             :      value for them that would still make it less than
    1321             :      global_insert_cnt.  Turns out, we can calculate that with just an
    1322             :      AND.  We need to make sure that in the rare case it is actually
    1323             :      2^24 away, we don't use a delta of 0.  We also want to make sure
    1324             :      that even with inexact float math, we never get to 1.0, so we bias
    1325             :      the exponent up slightly. */
    1326        4688 :   ulong last_ref = (ulong)info->last_ref;
    1327        4688 :   ulong delta = fd_ulong_max( (global_insert_cnt - last_ref) & 0xFFFFFFUL, 1UL );
    1328        4688 :   float ema_refs = ALPHA + powf( 1.0f-ALPHA, 0.05f+(float)delta ) * info->ema_refs;
    1329             : 
    1330        4688 :   info->ema_refs = ema_refs;
    1331        4688 :   info->last_ref = (uint)(global_insert_cnt & 0xFFFFFFUL);
    1332        4688 : #undef ALPHA
    1333             : 
    1334        4688 :   return ema_refs;
    1335        4688 : #endif
    1336        4688 : }
    1337             : 
    1338             : /* Finds addr in acct_map, making it ACTIVE if it isn't already */
    1339             : static inline ulong
    1340             : lookup_pubkey( fd_rdisp_t * disp,
    1341        4689 :                fd_acct_addr_t const * addr ) {
    1342             : 
    1343        4689 :     ulong idx = acct_map_idx_query( disp->acct_map, addr, ULONG_MAX, disp->acct_pool );
    1344        4689 :     acct_info_t * ai = NULL;
    1345        4689 :     if( FD_LIKELY( idx!=ULONG_MAX ) ) {
    1346         745 :       ai = disp->acct_pool+idx;
    1347         745 :       if( FD_LIKELY( ai->cached!=ACCT_INFO_CACHED ) ) return idx;
    1348             : 
    1349             :       /* CACHED -> ACTIVE transition.  It has the right key. last_ref,
    1350             :          and ema_refs are valid. states was zeroed when it was cached. */
    1351           0 :       free_dlist_idx_remove( disp->free_acct_dlist, idx, disp->acct_pool );
    1352           0 :       memset( ai->last_reference, '\0', sizeof(ai->last_reference) );
    1353           0 :       return idx;
    1354         745 :     }
    1355             : 
    1356             :     /* The acct pool is sized so that the list cannot be empty at this
    1357             :        point.  However, the element at the head might be CACHED with
    1358             :        a different pubkey. */
    1359        3944 :     if( FD_UNLIKELY( free_dlist_is_empty( disp->free_acct_dlist, disp->acct_pool ) ) ) FD_LOG_CRIT(( "rdisp account pool exhausted" ));
    1360        3944 :     idx = free_dlist_idx_peek_head( disp->free_acct_dlist, disp->acct_pool );
    1361        3944 :     ai = disp->acct_pool+idx;
    1362             : 
    1363             :     /* CACHED -> FREE transition */
    1364        3944 :     if( FD_LIKELY( ai->next!=0U ) ) {
    1365           0 :       acct_map_idx_remove_fast( disp->acct_map, idx, disp->acct_pool );
    1366           0 :     }
    1367             : 
    1368             :     /* FREE -> ACTIVE transition */
    1369        3944 :     ai->key      = *addr;
    1370        3944 :     ai->states   = 0U;
    1371        3944 :     ai->last_ref = 0U;
    1372        3944 :     ai->ema_refs = 0.0f;
    1373        3944 :     free_dlist_idx_remove( disp->free_acct_dlist, idx, disp->acct_pool );
    1374        3944 :     memset( ai->last_reference, '\0', sizeof(ai->last_reference) );
    1375        3944 :     acct_map_idx_insert( disp->acct_map, idx, disp->acct_pool );
    1376             : 
    1377        3944 :     return idx;
    1378        3944 : }
    1379             : 
    1380             : static void
    1381             : add_edges( fd_rdisp_t           * disp,
    1382             :            fd_rdisp_txn_t       * ele,
    1383             :            fd_acct_addr_t const * addrs,
    1384             :            ulong                  addr_cnt,
    1385             :            uint                   lane,
    1386             :            int                    writable,
    1387             :            int                    update_score,
    1388             :            fd_rdisp_neww_t *      new_wr,
    1389        6166 :            ulong                  new_wr_offset ) {
    1390             :   /* When we first start, the low 14 bits are 0x3FFF, so that gives us
    1391             :      w_cnt==0, r_cnt==0, as desired.  Then otherwise, the low 7 bits are
    1392             :      w_cnt_1, so adding 1 gives w_cnt.  If we've actually added
    1393             :      MAX_ACCT_PER_TXN writable accounts, then r_cnt will be incorrect,
    1394             :      but then we actually can't add any more readonly accounts, so the
    1395             :      function call will be a no-op. */
    1396             : 
    1397        6166 :   ulong w_cnt =  (ele->edge_cnt_etc + 1U)     & 0x7FU;
    1398        6166 :   ulong r_cnt = ((ele->edge_cnt_etc + 1U)>>7) & 0x7FU;
    1399        6166 :   ulong acct_idx = w_cnt +     r_cnt;
    1400        6166 :   ulong edge_idx = w_cnt + 3UL*r_cnt;
    1401             : 
    1402       10855 :   for( ulong i=0UL; i<addr_cnt; i++ ) {
    1403        4689 :     fd_acct_addr_t const * addr = addrs+i;
    1404        4689 :     acct_info_t * ai = NULL;
    1405             : 
    1406             :     /* Step 1: lookup the pubkey */
    1407        4689 :     ulong idx = lookup_pubkey( disp, addr );
    1408        4689 :     ai = disp->acct_pool+idx;
    1409             :     /* At this point, in all cases, the acct_info is now in the ACTIVE
    1410             :        state.  It's in acct_map and not in free_acct_dlist. */
    1411             : 
    1412             :     /* Assume that transactions are drawn randomly from some large
    1413             :        distribution of potential transactions.  We want to estimate the
    1414             :        expected value of the probability that this transaction conflicts
    1415             :        with the next transaction that is sampled.  Two transactions
    1416             :        conflict if they conflict on any account, and in general, they
    1417             :        conflict if they both reference the same account, unless both
    1418             :        this transaction and the next one only read it.  We don't have
    1419             :        read/write info, so the best guess we have is that the next
    1420             :        transaction does the same thing to the account that this one
    1421             :        does.  That means we only really care about the accounts that
    1422             :        this transaction writes to.  Label those a_1, a_2, ..., a_i, and
    1423             :        suppose the probability that the next transaction references a_i
    1424             :        is p_i (determined using the EMA).  Now then, assuming accounts
    1425             :        are independent (which is false, but whatever), then the
    1426             :        probability that the next transaction does not conflict with this
    1427             :        account is:
    1428             :                      (1-p_1) * (1-p_2) * (1 - p_i).
    1429             : 
    1430             :        Since for the treap, a lower value means we'll schedule it
    1431             :        earlier, we'll use the probability of non-conflict as the
    1432             :        fractional part of the score. */
    1433        4689 :     if( FD_LIKELY( update_score ) ) {
    1434        4688 :       float score_change = 1.0f - update_ema( ai, disp->global_insert_cnt );
    1435        4688 :       ele->score *= fd_float_if( writable, score_change, 1.0f );
    1436        4688 :     }
    1437             : 
    1438             :     /* Step 2: add edge. There are 4 cases depending on whether this is
    1439             :        a writer or not and whether the previous reference was a writer
    1440             :        or not. */
    1441        4689 :     int      _ignore;
    1442             : 
    1443        4689 :     edge_t   ref_to_pa = ai->last_reference[ lane ];
    1444        4689 :     ulong    displ0    = ((ulong)ele - (ulong)disp->pool     )/sizeof(fd_rdisp_txn_t);
    1445        4689 :     ulong    displ1    = ((ulong)ele - (ulong)disp->ptxn_pool)/sizeof(fd_rdisp_ptxn_t);
    1446        4689 :     edge_t   ref_to_me = (uint)fd_ulong_if( displ0>disp->depth, EDGE_MAKE_SPECIAL(PTXN, displ1), (displ0<<8)|acct_idx );
    1447        4689 :     edge_t * pa        = FOLLOW_EDGE( disp, ai->last_reference[ lane ], _ignore );
    1448        4689 :     edge_t * me        = ele->edges + edge_idx;
    1449             : 
    1450             :     /* In the case that this is the first txn in the DAG, pa will point
    1451             :        to edges[0] of the sentinel element, pool[0].  We don't care
    1452             :        about what is stored there, so just set it up as a dummy element
    1453             :        to make the rest of the code work properly in this case too.  If
    1454             :        this is a read, we want me->sibli to ref_to_me in case 4; if this
    1455             :        is a write, we want to set me->sibli to 0 in case 2, but we need
    1456             :        to make sure pb==pa. */
    1457        4689 :     disp->pool->edges[0] = EDGE_MAKE_SPECIAL(LAST, (uint)idx);
    1458        4689 :     disp->pool->edges[1] = fd_uint_if( writable, 0U, ref_to_me );
    1459        4689 :     disp->pool->edges[2] = fd_uint_if( writable, 0U, ref_to_me );
    1460             : 
    1461        4689 :     int states = ai->states;
    1462             : 
    1463        4689 :     if( writable ) { /* also should be known at compile time */
    1464        3112 :       if( ACCT_INFO_STATE_IS( states, lane, LAST_REF_WAS_WRITE ) ) { /* unclear prob */
    1465             :         /* Case 1: w-w. The parent is the special last pointer.  Point
    1466             :            the parent to me, and set me to the last pointer. */
    1467         224 :         *me = *pa;
    1468         224 :         *pa = ref_to_me;
    1469        2888 :       } else {
    1470             :         /* Case 2: r-w. This is the tricky case because there could be
    1471             :            multiple readers.  We need to set all the last readers' child
    1472             :            pointers to me. */
    1473        2888 :         *me = *pa;
    1474        2888 :         *pa = ref_to_me;
    1475        2888 :         edge_t * pb = FOLLOW_EDGE( disp, pa[1], _ignore );
    1476             :         /* Intentionally skip the first in_degree increment, because it
    1477             :            will be done later */
    1478        2888 :         while( pb!=pa ) {
    1479           0 :           *pb = ref_to_me;
    1480           0 :           ele->in_degree++;
    1481           0 :           pb = FOLLOW_EDGE( disp, pb[1], _ignore );
    1482           0 :         }
    1483        2888 :         states = ACCT_INFO_STATE_SET( states, lane, LAST_REF_WAS_WRITE );
    1484        2888 :       }
    1485             :       /* An account in the ready class is getting rewritten.  So we bump
    1486             :          it out of the ready class. */
    1487        3112 :       if( fd_rdisp_mq_test( disp->mq, idx, lane ) && fd_rdisp_mq_test_prio0( disp->mq, idx, lane ) ) fd_rdisp_mq_adjust( disp->mq, idx, lane, mq_prio( disp, ai, 0 ) );
    1488        3112 :     } else {
    1489        1577 :       if( ACCT_INFO_STATE_IS( states, lane, LAST_REF_WAS_WRITE ) ) { /* unclear prob */
    1490             :         /* Case 3: w-r. Similar to case 1, but need to initialize my
    1491             :            next and prev sibling pointers too. */
    1492           0 :         *me = *pa;
    1493           0 :         *pa = ref_to_me;
    1494           0 :         me[1] = ref_to_me; /* next */
    1495           0 :         me[2] = ref_to_me; /* prev */
    1496           0 :         states = ACCT_INFO_STATE_SET( states, lane, LAST_REF_WAS_READ );
    1497        1577 :       } else {
    1498             :         /* Case 4: r-r. Add myself as a sibling instead of a child */
    1499        1577 :         *me = *pa;
    1500        1577 :         FOLLOW_EDGE( disp, pa[1], _ignore )[2] = ref_to_me;  /* prev->next->prev = me   */
    1501        1577 :         me[1] = pa[1];                                       /* me->next   = prev->next */
    1502        1577 :         me[2] = ref_to_pa;                                   /* me->prev   = prev       */
    1503        1577 :         pa[1] = ref_to_me;                                   /* prev->next = me         */
    1504        1577 :       }
    1505        1577 :     }
    1506             : 
    1507             :     /* Step 3: Update new_wr */
    1508        4689 :     if( ((!!writable) & FD_LIKELY( !!new_wr )) && !fd_rdisp_mq_test( disp->mq, idx, lane ) ) {
    1509        2889 :       if( FD_UNLIKELY( fd_rdisp_mq_cnt( disp->mq, lane )>=MAX_WRITERS_PER_BLOCK ) ) FD_LOG_CRIT(( "rdisp lane %u writable account set full", lane ));
    1510        2889 :       fd_rdisp_mq_insert( disp->mq, idx, lane, mq_prio( disp, ai, 0 ) );
    1511        2889 :       fd_rdisp_neww_insert( new_wr, new_wr_offset+i );
    1512        2889 :     }
    1513             : 
    1514             :     /* Step 4: Update the final values */
    1515             :     /* In general, we want to increment the in_degree unless this
    1516             :        transaction is the first to reference this account.  The
    1517             :        exception is that if this account has only readers, including
    1518             :        this transaction, we don't want to increment the in_degree
    1519             :        either.  At this point, we can tell if that is the case based on
    1520             :        states.  */
    1521        4689 :     int any_write = ACCT_INFO_STATE_IS( states, lane, LAST_REF_WAS_READ ) | ACCT_INFO_STATE_IS( states, lane, LAST_REF_WAS_WRITE );
    1522        4689 :     ele->in_degree += (uint)((ai->last_reference[ lane ]!=0U) & any_write);
    1523        4689 :     ai->last_reference[ lane ] = ref_to_me;
    1524        4689 :     ai->states                 = (uchar)states;
    1525        4689 :     edge_idx += fd_uint_if( writable, 1U, 3U );
    1526        4689 :     acct_idx++;
    1527        4689 :   }
    1528             :   /* Can't overflow by construction, except for the intentional overflow on the first time. */
    1529        6166 :   ele->edge_cnt_etc += (uint)addr_cnt<<fd_int_if( writable, 0, 7 );
    1530        6166 : }
    1531             : 
    1532             : 
    1533             : /* should be called with all writable accounts first */
    1534             : static void
    1535             : add_unstaged_edges( fd_rdisp_t * disp,
    1536             :                     fd_rdisp_txn_t       * ele,
    1537             :                     fd_rdisp_unstaged_t  * unstaged,
    1538             :                     fd_acct_addr_t const * addr,
    1539             :                     ulong                  addr_cnt,
    1540             :                     int                    writable,
    1541             :                     int                    update_score,
    1542             :                     fd_rdisp_neww_t      * new_wr,
    1543           0 :                     ulong                  new_wr_offset ) {
    1544           0 :   ulong base_idx = unstaged->writable_cnt+unstaged->readonly_cnt;
    1545           0 :   FD_TEST( !writable || unstaged->readonly_cnt==0U );
    1546           0 :   for( ulong i=0UL; i<addr_cnt; i++ ) {
    1547           0 :     unstaged->keys[ base_idx+i ] = addr[i];
    1548           0 :     if( FD_LIKELY( update_score ) ) {
    1549           0 :       ulong idx = acct_map_idx_query( disp->acct_map, addr+i, ULONG_MAX, disp->acct_pool );
    1550             :       /* since these are unstaged, we don't bother moving accounts
    1551             :          around */
    1552           0 :       float score_change = 1.0f;
    1553           0 :       if( FD_LIKELY( idx!=ULONG_MAX ) ) score_change = 1.0f - update_ema( disp->acct_pool+idx, disp->global_insert_cnt );
    1554           0 :       ele->score *= fd_float_if( writable & update_score, score_change, 1.0f );
    1555           0 :     }
    1556           0 :     if( ((!!writable) & FD_LIKELY( !!new_wr )) ) {
    1557           0 :       ustlt_key_t query[1] = {{
    1558           0 :         .block_idx = unstaged->block_idx,
    1559           0 :         .acct      = addr[i]
    1560           0 :       }};
    1561           0 :       int new = (ustlt_map_idx_query( disp->ustlt_map, query, ULONG_MAX, disp->ustlt_pool )==ULONG_MAX);
    1562           0 :       fd_rdisp_neww_insert_if( new_wr, new, new_wr_offset+i );
    1563           0 :       if( FD_UNLIKELY( new ) ) {
    1564           0 :         if( FD_UNLIKELY( ustlt_dlist_is_empty( disp->ustlt_free, disp->ustlt_pool ) ) ) FD_LOG_CRIT(( "rdisp unstaged LtHash entry pool exhausted" ));
    1565           0 :         ulong ustlt_idx = ustlt_dlist_idx_pop_head( disp->ustlt_free, disp->ustlt_pool );
    1566           0 :         disp->ustlt_pool[ ustlt_idx ].key = *query;
    1567           0 :         ustlt_map_idx_insert( disp->ustlt_map, ustlt_idx, disp->ustlt_pool );
    1568           0 :         ustlt_dlist_idx_push_tail( disp->block_pool[ unstaged->block_idx ].ustlt_needed, ustlt_idx, disp->ustlt_pool );
    1569           0 :       }
    1570           0 :     }
    1571           0 :   }
    1572           0 :   *(fd_ptr_if( writable, &(unstaged->writable_cnt), &(unstaged->readonly_cnt) ) ) += (uint)addr_cnt;
    1573           0 : }
    1574             : 
    1575             : /* prefetch_accts warms what add_edges (or add_unstaged_edges) touches
    1576             :    first for each of the cnt addresses: the chain head of each account,
    1577             :    then the element at the head of each chain, which is where the key
    1578             :    compare of a hit usually lands (queries move the found element to the
    1579             :    front).  The map is large enough that these are otherwise serial
    1580             :    cache misses, one account at a time.  The free list head, which a
    1581             :    miss takes, is warmed by the caller.  Hints only: the queries below
    1582             :    are unchanged.
    1583             : 
    1584             :    always_inline is load bearing: this only loads and prefetches, and
    1585             :    gcc treats __builtin_prefetch as side effect free, so it finds the
    1586             :    function to be (looping) pure and deletes the call, prefetches and
    1587             :    all.  Clang keeps the prefetches but only inlines by heuristic. */
    1588             : 
    1589             : static inline __attribute__((always_inline)) void
    1590             : prefetch_accts( fd_rdisp_t const *     disp,
    1591             :                 fd_acct_addr_t const * addrs,
    1592        1541 :                 ulong                  cnt ) {
    1593        1541 :   uint const * heads[ MAX_ACCT_PER_TXN ];
    1594        1541 :   uint const * chain = acct_map_private_chain_const( disp->acct_map );
    1595        1541 :   ulong seed         = acct_map_seed     ( disp->acct_map );
    1596        1541 :   ulong chain_cnt    = acct_map_chain_cnt( disp->acct_map );
    1597        6226 :   for( ulong i=0UL; i<cnt; i++ ) {
    1598        4685 :     heads[ i ] = chain + acct_map_private_chain_idx( addrs+i, seed, chain_cnt );
    1599        4685 :     __builtin_prefetch( heads[ i ], 0, 3 );
    1600        4685 :   }
    1601        6225 :   for( ulong i=0UL; i<cnt; i++ ) {
    1602        4684 :     ulong head = (ulong)*heads[ i ];
    1603        4684 :     __builtin_prefetch( disp->acct_pool + fd_ulong_if( head==UINT_MAX, 0UL, head ), 0, 3 );
    1604        4684 :   }
    1605        1541 : }
    1606             : 
    1607             : ulong
    1608        3084 : fd_rdisp_peek_free_txn( fd_rdisp_t const * disp ) {
    1609        3084 :   ulong idx = pool_private_meta_const( disp->pool )->free_top;
    1610        3084 :   return fd_ulong_if( idx==pool_idx_null( disp->pool ), 0UL, idx );
    1611        3084 : }
    1612             : 
    1613             : ulong
    1614             : fd_rdisp_add_txn( fd_rdisp_t          *  disp,
    1615             :                   FD_RDISP_BLOCK_TAG_T   insert_block,
    1616             :                   fd_txn_t const       * txn,
    1617             :                   uchar const          * payload,
    1618             :                   fd_acct_addr_t const * alts,
    1619             :                   int                    serializing,
    1620        1540 :                   fd_rdisp_neww_t      * new_writable ) {
    1621        1540 :   if( FD_LIKELY( new_writable ) ) fd_rdisp_neww_new( new_writable ); /* clear it */
    1622             : 
    1623        1540 :   fd_rdisp_blockinfo_t * block   = block_map_ele_query( disp->blockmap, &insert_block, NULL, disp->block_pool );
    1624        1540 :   if( FD_UNLIKELY( !block || !block->insert_ready ) ) return 0UL;
    1625        1540 :   if( FD_UNLIKELY( !pool_free( disp->pool       ) ) ) return 0UL;
    1626             : 
    1627        1540 :   fd_acct_addr_t const * imm_addrs = fd_txn_get_acct_addrs( txn, payload );
    1628             : 
    1629        1540 :   /*                   */ prefetch_accts( disp, imm_addrs, fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_IMM ) );
    1630        1540 :   if( FD_LIKELY( alts ) ) prefetch_accts( disp, alts, fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_ALT ) );
    1631        1540 :   __builtin_prefetch( disp->acct_pool + free_dlist_idx_peek_head( disp->free_acct_dlist, disp->acct_pool ), 1, 3 );
    1632             : 
    1633        1540 :   ulong idx = pool_idx_acquire( disp->pool );
    1634        1540 :   fd_rdisp_txn_t * rtxn = disp->pool + idx;
    1635        1540 :   if( FD_UNLIKELY( rtxn->in_degree!=IN_DEGREE_FREE ) ) FD_LOG_CRIT(( "pool[%lu].in_degree==%u but free", idx, rtxn->in_degree ));
    1636             : 
    1637             :   /* The element the next add_txn takes (unless a completion pushes a
    1638             :      recently touched one first) is the next cold line in this path. */
    1639        1540 :   __builtin_prefetch( disp->pool + fd_rdisp_peek_free_txn( disp ), 1, 3 );
    1640             : 
    1641        1540 :   ulong ws = fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_WRITABLE_SIGNER );
    1642        1540 :   ulong rs = fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_READONLY_SIGNER );
    1643        1540 :   ulong wn = fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_WRITABLE_NONSIGNER_IMM );
    1644        1540 :   ulong rn = fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_READONLY_NONSIGNER_IMM );
    1645        1540 :   ulong wa = fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_WRITABLE_ALT );
    1646        1540 :   ulong ra = fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_READONLY_ALT );
    1647             : 
    1648        1540 :   if( FD_UNLIKELY( !block->staged ) ) {
    1649           0 :     rtxn->in_degree = IN_DEGREE_UNSTAGED;
    1650           0 :     rtxn->score     = 1.0f;
    1651             : 
    1652           0 :     fd_rdisp_unstaged_t * unstaged = disp->unstaged + idx;
    1653           0 :     unstaged->block_idx    = (uint)(block-disp->block_pool);
    1654           0 :     unstaged->writable_cnt = 0U;
    1655           0 :     unstaged->readonly_cnt = 0U;
    1656             : 
    1657           0 :     add_unstaged_edges(   disp, rtxn, unstaged, imm_addrs,          ws, 1, 1, new_writable, 0UL            );
    1658           0 :     add_unstaged_edges(   disp, rtxn, unstaged, imm_addrs+ws+rs,    wn, 1, 1, new_writable, ws+rs          );
    1659           0 :     if( FD_LIKELY( alts ) )
    1660           0 :       add_unstaged_edges( disp, rtxn, unstaged, alts,               wa, 1, 1, new_writable, ws+rs+wn+rn    );
    1661           0 :     add_unstaged_edges(   disp, rtxn, unstaged, imm_addrs+ws,       rs, 0, 1, new_writable, ws             );
    1662           0 :     add_unstaged_edges(   disp, rtxn, unstaged, imm_addrs+ws+rs+wn, rn, 0, 1, new_writable, ws+rs+wn       );
    1663           0 :     if( FD_LIKELY( alts ) )
    1664           0 :       add_unstaged_edges( disp, rtxn, unstaged, alts+wa,            ra, 0, 1, new_writable, ws+rs+wn+rn+wa );
    1665             : 
    1666           0 :     unstaged_txn_ll_ele_push_tail( block->ll, rtxn, disp->pool );
    1667        1540 :   } else {
    1668        1540 :     uint lane = block->staging_lane;
    1669             : 
    1670        1540 :     rtxn->in_degree    = 0U;
    1671        1540 :     rtxn->score        = 1.0f;
    1672             :     /* There's not a good way to initialize w_cnt_1 to -1, which is what
    1673             :        it should be at this point, but this is close.  We must be sure
    1674             :        to add at least one writer (which we are assured of because we
    1675             :        know the transaction passed fd_txn_parse) to correct this value. */
    1676        1540 :     rtxn->edge_cnt_etc = ((block->linear_block_number<<16) | (lane<<14)) - 1U;
    1677             : 
    1678        1540 :     add_edges(   disp, rtxn, imm_addrs,          ws, lane, 1, 1, new_writable, 0UL            );
    1679        1540 :     add_edges(   disp, rtxn, imm_addrs+ws+rs,    wn, lane, 1, 1, new_writable, ws+rs          );
    1680        1540 :     if( FD_LIKELY( alts ) )
    1681           0 :       add_edges( disp, rtxn, alts,               wa, lane, 1, 1, new_writable, ws+rs+wn+rn    );
    1682        1540 :     add_edges(   disp, rtxn, imm_addrs+ws,       rs, lane, 0, 1, new_writable, ws             );
    1683        1540 :     add_edges(   disp, rtxn, imm_addrs+ws+rs+wn, rn, lane, 0, 1, new_writable, ws+rs+wn       );
    1684        1540 :     if( FD_LIKELY( alts ) )
    1685           0 :       add_edges( disp, rtxn, alts+wa,            ra, lane, 0, 1, new_writable, ws+rs+wn+rn+wa );
    1686        1540 :   }
    1687        1540 :   if( FD_UNLIKELY( rtxn->score>FD_RDISP_MAX_SCORE ) ) rtxn->score=FD_RDISP_MAX_SCORE;
    1688             : 
    1689        1540 :   if( FD_UNLIKELY( serializing | block->last_insert_was_serializing ) ) {
    1690           0 :     block->last_serializing = block->inserted_cnt;
    1691           0 :   }
    1692        1540 :   block->last_insert_was_serializing = (uint)!!serializing;
    1693        1540 :   rtxn->score = fd_rdisp_score( rtxn->score, block->last_serializing );
    1694             : 
    1695        1540 :   block->inserted_cnt++;
    1696        1540 :   disp->global_insert_cnt++;
    1697             : 
    1698        1540 :   if( FD_LIKELY( (block->staged) & (rtxn->in_degree==0U) ) ) {
    1699        1430 :     pending_prq_ele_t temp[1] = {{ .score = rtxn->score, .linear_block_number = block->linear_block_number, .txn_idx = (uint)idx }};
    1700        1430 :     pending_prq_insert( disp->lanes[ block->staging_lane ].pending, temp );
    1701        1430 :   }
    1702             : 
    1703        1540 :   return idx;
    1704        1540 : }
    1705             : 
    1706             : ulong
    1707             : fd_rdisp_get_next_ready( fd_rdisp_t           * disp,
    1708           0 :                          FD_RDISP_BLOCK_TAG_T   schedule_block ) {
    1709           0 :   fd_rdisp_blockinfo_t * block   = block_map_ele_query( disp->blockmap, &schedule_block, NULL, disp->block_pool );
    1710           0 :   if( FD_UNLIKELY( !block || !block->schedule_ready ) ) return 0UL;
    1711             : 
    1712           0 :   ulong idx;
    1713           0 :   if( FD_LIKELY( block->staged ) ) {
    1714           0 :     ulong staging_lane = block->staging_lane;
    1715           0 :     per_lane_info_t * l = disp->lanes + staging_lane;
    1716             : 
    1717           0 :     if( FD_UNLIKELY( !pending_prq_cnt( l->pending )                                ) ) return 0UL;
    1718           0 :     if( FD_UNLIKELY( l->pending->linear_block_number != block->linear_block_number ) ) return 0UL;
    1719             :     /* e.g. when completed_cnt==0, we can accept any score below 1.0 */
    1720           0 :     if( FD_UNLIKELY( l->pending->score>=(float)(block->completed_cnt+1U)           ) ) return 0UL;
    1721           0 :     idx = l->pending->txn_idx;
    1722           0 :     pending_prq_remove_min( l->pending );
    1723           0 :     if( FD_UNLIKELY( EDGE_IS_PTXN(idx) ) ) disp->ptxn_pool[ EDGE_SPECIAL_IDX( idx ) ].in_degree = IN_DEGREE_DISPATCHED;
    1724           0 :     else                                   disp->pool     [                   idx   ].in_degree = IN_DEGREE_DISPATCHED;
    1725           0 :   } else {
    1726           0 :     if( FD_UNLIKELY( block->dispatched_cnt!=block->completed_cnt       ) ) return 0UL;
    1727           0 :     if( FD_UNLIKELY( !unstaged_txn_ll_is_empty( block->ll, disp->pool ) ) ) {
    1728           0 :       idx = unstaged_txn_ll_idx_peek_head( block->ll, disp->pool );
    1729           0 :       disp->pool[ idx ].in_degree = IN_DEGREE_UNSTAGED_DISPATCHED;
    1730           0 :     } else if( FD_UNLIKELY( !ustlt_dlist_is_empty( block->ustlt_created, disp->ustlt_pool ) ) ) {
    1731           0 :       idx = ustlt_dlist_ele_peek_head( block->ustlt_created, disp->ustlt_pool )->ptxn_idx;
    1732           0 :       disp->ptxn_pool[ idx ].in_degree = IN_DEGREE_UNSTAGED_DISPATCHED;
    1733           0 :       idx = EDGE_MAKE_SPECIAL(PTXN, idx);
    1734           0 :     } else {
    1735           0 :       return 0UL;
    1736           0 :     }
    1737           0 :   }
    1738           0 :   block->dispatched_cnt++;
    1739             : 
    1740           0 :   return idx;
    1741           0 : }
    1742             : 
    1743             : void
    1744             : fd_rdisp_complete_txn( fd_rdisp_t * disp,
    1745             :                        ulong        txn_idx,
    1746           0 :                        int          reclaim ) {
    1747             : 
    1748           0 :   int ptxn = EDGE_IS_PTXN( txn_idx );
    1749           0 :   fd_rdisp_txn_t * rtxn = (!ptxn) ? disp->pool + txn_idx :
    1750           0 :                                     (fd_rdisp_txn_t *)fd_type_pun( (disp->ptxn_pool + EDGE_SPECIAL_IDX(txn_idx)) );
    1751           0 :   fd_rdisp_blockinfo_t * block = NULL;
    1752             : 
    1753           0 :   if( FD_UNLIKELY( rtxn->in_degree==IN_DEGREE_UNSTAGED_DISPATCHED ) ) {
    1754             :     /* Unstaged */
    1755           0 :     if( FD_UNLIKELY( ptxn ) ) {
    1756           0 :       fd_rdisp_ptxn_t  * ptxn_e    = disp->ptxn_pool + EDGE_SPECIAL_IDX(txn_idx); /* == rtxn */
    1757           0 :       ustlt_t          * ustlt_ele = disp->ustlt_pool + ptxn_e->last;
    1758           0 :       block = disp->block_pool + ustlt_ele->key.block_idx;
    1759           0 :       FD_TEST( ustlt_ele==ustlt_dlist_ele_pop_head( block->ustlt_created, disp->ustlt_pool ) );
    1760             :       /* ptxn_e gets freed below */
    1761           0 :       ustlt_dlist_ele_push_tail( disp->ustlt_free, ustlt_ele, disp->ustlt_pool );
    1762             : 
    1763           0 :     } else {
    1764           0 :       block = disp->block_pool + disp->unstaged[ txn_idx ].block_idx;
    1765           0 :       FD_TEST( rtxn==unstaged_txn_ll_ele_peek_head( block->ll, disp->pool ) );
    1766           0 :       unstaged_txn_ll_ele_pop_head( block->ll, disp->pool );
    1767           0 :     }
    1768           0 :     block->completed_cnt++;
    1769           0 :   } else if( FD_LIKELY( rtxn->in_degree==IN_DEGREE_DISPATCHED ) ) {
    1770             :     /* Staged */
    1771           0 :     ulong w_cnt = fd_ulong_if( ptxn, 0UL, 1UL + ((rtxn->edge_cnt_etc    ) & 0x7FU) );
    1772           0 :     ulong r_cnt = (rtxn->edge_cnt_etc>> 7) & 0x7FU;
    1773           0 :     ulong lane  = (rtxn->edge_cnt_etc>>14) & 0x3U;
    1774           0 :     uint  tail_linear_block_num = (uint)(disp->lanes[lane].linear_block_number);
    1775           0 :     ulong edge_idx = 0UL;
    1776           0 :     for( ulong i=0UL; i<w_cnt+r_cnt; i++ ) {
    1777           0 :       edge_t const * e = rtxn->edges+edge_idx;
    1778           0 :       edge_t const  e0 = *e;
    1779           0 :       edge_t ref_to_me = (uint)fd_ulong_if( ptxn, txn_idx, ((txn_idx<<8) | i) );
    1780             : 
    1781             :       /* To help with explanations, consider the following DAG:
    1782             : 
    1783             :            --> B --\   --> E --\
    1784             :           /         V /         V
    1785             :          A           D         (G)
    1786             :           \         ^ \         ^
    1787             :            --> C --/   --> F --/     */
    1788             : 
    1789           0 :       if( FD_UNLIKELY( EDGE_IS_LAST( e0 ) ) ) {
    1790           0 :         ulong acct_idx = EDGE_SPECIAL_IDX( e0 );
    1791           0 :         acct_info_t * ai = disp->acct_pool + acct_idx;
    1792             :         /* If this is a writer, e.g. node G above, we know it's the last
    1793             :            one, so we can clear last_reference.  We know it has to go to
    1794             :            the NEEDS_PTXN state unless LtHash tracking was disabled for it.
    1795             :            If this is a reader, e.g. node E and F above, if node G
    1796             :            didn't exist, we need to check if it's the last one
    1797             :            (me==me->next).  If so, we can clear last_reference.  If not,
    1798             :            we need to delete this node from the linked list.  In either
    1799             :            case, there are only readers or pseudo-transactions, so we're
    1800             :            either in NO_WRITERS or NEEDS_PTXN states, and we stay in
    1801             :            that state. */
    1802           0 :         if( edge_idx<w_cnt ) {
    1803           0 :           ai->last_reference[ lane ] = 0U;
    1804           0 :           int needs_ptxn = fd_rdisp_mq_test( disp->mq, acct_idx, lane );
    1805           0 :           ai->states = fd_uchar_if( needs_ptxn, ACCT_INFO_STATE_SET(ai->states, lane, NEEDS_PTXN), ACCT_INFO_STATE_SET(ai->states, lane, NO_WRITERS) );
    1806             :           /* The account's last writer is done, so we bump it into the
    1807             :              ready class, hotness threshold permitting. */
    1808           0 :           if( needs_ptxn && ai->ema_refs<disp->ready_acct_ema_max ) fd_rdisp_mq_adjust( disp->mq, acct_idx, lane, 0.0f );
    1809           0 :         } else if( e[1]==ref_to_me ) {
    1810           0 :           ai->last_reference[ lane ] = 0U;
    1811           0 :         } else {
    1812           0 :           int _ignore;
    1813           0 :           FOLLOW_EDGE( disp, e[1], _ignore )[2] = e[2];  /* me->next->prev = me->prev */
    1814           0 :           FOLLOW_EDGE( disp, e[2], _ignore )[1] = e[1];  /* me->prev->next = me->next */
    1815           0 :           ai->last_reference[ lane ]= fd_uint_if( ai->last_reference[ lane ]==ref_to_me, e[1], ai->last_reference[ lane ] );
    1816           0 :         }
    1817             : 
    1818             :         /* Potentially transition from ACTIVE -> CACHED */
    1819           0 :         if( FD_UNLIKELY( (ai->states==0)&
    1820           0 :                          (ai->last_reference[ 0 ]==0U)&(ai->last_reference[ 1 ]==0U)&
    1821           0 :                          (ai->last_reference[ 2 ]==0U)&(ai->last_reference[ 3 ]==0U) ) ) {
    1822           0 :           ai->cached = ACCT_INFO_CACHED;
    1823           0 :           free_dlist_idx_push_tail( disp->free_acct_dlist, acct_idx, disp->acct_pool );
    1824           0 :         }
    1825           0 :       } else {
    1826           0 :         int child_is_writer;
    1827             : 
    1828           0 :         edge_t next_e = e0;
    1829           0 :         edge_t const * child_edge;
    1830           0 :         while( 1 ) {
    1831             :           /* This loop first traverses the me->child link, and then
    1832             :              traverses any sibling links.  For example, in the case that
    1833             :              we're completing node D above, the first child_txn is E,
    1834             :              and the second child_txn is F.  child_txn might be a
    1835             :              pseudo-transaction. */
    1836           0 :           /*            */ child_edge = FOLLOW_EDGE(     disp, next_e, child_is_writer );
    1837           0 :           fd_rdisp_txn_t * child_txn  = FOLLOW_EDGE_TXN( disp, next_e                  );
    1838             : 
    1839             :           /* Sanity test */
    1840           0 :           FD_TEST( child_txn->in_degree>0U                   );
    1841           0 :           FD_TEST( child_txn->in_degree<IN_DEGREE_DISPATCHED );
    1842             : 
    1843           0 :           if( FD_UNLIKELY( 0U==(--(child_txn->in_degree)) ) ) {
    1844             :             /* We need an operation something like
    1845             :                fd_frag_meta_ts_decomp. child_txn has the low 16 bits,
    1846             :                and tail_linear_block_num has the full 32 bits, except
    1847             :                for tail_linear_block_num refers to a block < block_depth
    1848             :                later.  Since block_depth<2^16, that means we can resolve
    1849             :                this unambiguously.  Basically, we copy the high 16 bits
    1850             :                frorm tail_linear_block_num unless that would make
    1851             :                linear_block_num larger than tail_linear_block_num, in
    1852             :                which case, we subtract 2^16. */
    1853           0 :             uint low_16_bits = child_txn->edge_cnt_etc>>16;
    1854           0 :             uint linear_block_num = ((tail_linear_block_num & ~0xFFFFU) | low_16_bits) - (uint)((low_16_bits>(tail_linear_block_num&0xFFFFU))<<16);
    1855           0 :             pending_prq_ele_t temp[1] = {{ .score               = child_txn->score,
    1856           0 :                                            .linear_block_number = linear_block_num,
    1857           0 :                                            .txn_idx             = fd_uint_if( EDGE_IS_PTXN(next_e), next_e, next_e>>8 ) }};
    1858           0 :             pending_prq_insert( disp->lanes[ lane ].pending, temp );
    1859           0 :           }
    1860           0 :           if( child_is_writer || child_edge[1]==e0 ) break;
    1861           0 :           next_e = child_edge[1];
    1862           0 :         }
    1863             :         /* In the case that the completed transaction is a reader, say B
    1864             :            or C above, it seems like we should need to remove it from
    1865             :            the doubly linked list, but we actually don't.  The times
    1866             :            that we need to read the sibling pointers are:
    1867             :             1. Completing the writer before a reader (e.g. completing A)
    1868             :             2. Completing a reader that's the last in the DAG (e.g.
    1869             :                completing E/F if G didn't exist)
    1870             :             3. Adding another reader to the same set of readers (e.g. if
    1871             :                G were added as a reader instead of a writer)
    1872             :             4. Adding a writer after a set of readers (e.g. adding G).
    1873             : 
    1874             :            Supposing that the completed transaction is a reader, since
    1875             :            we checked EDGE_IS_LAST( e0 ), we know that there is at least
    1876             :            one writer that follows this reader, e.g. D above.
    1877             : 
    1878             :            And so none of these reason can apply to this group of readers:
    1879             :             1. Completing B or C implies that A has already completed,
    1880             :                so it can't complete again.
    1881             :             2. We know that we're not in that case because we checked
    1882             :                EDGE_IS_LAST( e0 ), and if we're not last now, we cannot
    1883             :                become last later, because the growth happens in the
    1884             :                other direction.
    1885             :             3. Similarly, because we checked EDGE_IS_LAST, any future
    1886             :                additions of readers won't be to this group of readers.
    1887             :             4. Similarly, we know that there already is a writer that
    1888             :                follows this group of readers, so a later writer would
    1889             :                not read this set of readers.
    1890             : 
    1891             :            So then, we don't need to deal with the sibling edges.  The
    1892             :            fact that we only need to do it in one case almost calls into
    1893             :            question whether we need to maintain the whole circular
    1894             :            system in the first place, and whether we could get away with
    1895             :            a reference count or something instead, but it's important in
    1896             :            one critical case: suppose we add a bunch of readers, then
    1897             :            some of them complete, and then we add a writer (case 4
    1898             :            above).  We need to be able to enumerate the nodes in the DAG
    1899             :            that have not yet completed, and we need to be able to remove
    1900             :            them from that set in O(1).  Those two requirements don't
    1901             :            leave us with many alternatives besides a doubly linked list. */
    1902             : 
    1903           0 :         if( FD_UNLIKELY( EDGE_IS_LAST( *child_edge ) ) ) {
    1904             :           /* For example, either:
    1905             :              1. completing D if G didn't exist
    1906             :              2. completing E or F with G in the DAG
    1907             : 
    1908             :              After completing this transaction, there's either 0 writers
    1909             :              left (case 1) or 1 writer left (case 2) in the DAG. and if
    1910             :              there is one, it is the last reference.  In case 1, we need
    1911             :              to know if we have a pending LtHash pseudo-transaction to
    1912             :              determine whether we transition to state NO_WRITERS or
    1913             :              NEEDS_PTXN.  In case 2, we must be in LAST_REF_WAS_WRITE
    1914             :              and we stay there. */
    1915             : 
    1916           0 :           ulong acct_idx = EDGE_SPECIAL_IDX( *child_edge );
    1917           0 :           acct_info_t * ai = disp->acct_pool + acct_idx;
    1918           0 :           uint s = ai->states;
    1919           0 :           if( !ACCT_INFO_STATE_IS( s, lane, LAST_REF_WAS_WRITE ) ) {
    1920           0 :             int needs_ptxn = fd_rdisp_mq_test( disp->mq, acct_idx, lane );
    1921           0 :             s = fd_uint_if( needs_ptxn, ACCT_INFO_STATE_SET(s, lane, NEEDS_PTXN), ACCT_INFO_STATE_SET(s, lane, NO_WRITERS) );
    1922           0 :             ai->states = (uchar)s;
    1923             :             /* The account's last writer is done, so we bump it into the
    1924             :                ready class, hotness threshold permitting. */
    1925           0 :             if( needs_ptxn && ai->ema_refs<disp->ready_acct_ema_max ) fd_rdisp_mq_adjust( disp->mq, acct_idx, lane, 0.0f );
    1926           0 :           }
    1927           0 :         }
    1928           0 :       }
    1929           0 :       edge_idx += fd_ulong_if( i<w_cnt, 1UL, 3UL );
    1930           0 :     }
    1931           0 :     block = block_slist_ele_peek_head( disp->lanes[ lane ].block_ll, disp->block_pool );
    1932           0 :     block->completed_cnt++;
    1933           0 :   } else if( FD_LIKELY( rtxn->in_degree==IN_DEGREE_ZOMBIE ) ) {
    1934           0 :     FD_TEST( reclaim );
    1935           0 :     FD_TEST( !ptxn   );
    1936           0 :     block = block_pool_ele( disp->block_pool, rtxn->block_idx );
    1937           0 :     zombie_dlist_ele_remove( block->zombie_list, rtxn, disp->pool );
    1938             :     /* Fall through to the pool release branch below. */
    1939           0 :   } else {
    1940           0 :     FD_LOG_CRIT(( "completed un-dispatched transaction %lu", txn_idx ));
    1941           0 :   }
    1942           0 :   if( reclaim|ptxn ) {
    1943             :     /* For testing purposes, to make sure we don't read a completed
    1944             :        transaction, we can clobber the memory. */
    1945             :     /* memset( disp->pool+txn_idx, '\xCC', sizeof(fd_rdisp_txn_t) ); */
    1946           0 :     rtxn->in_degree = IN_DEGREE_FREE;
    1947           0 :     if( FD_LIKELY( !ptxn ) ) pool_idx_release     ( disp->pool,                        txn_idx   );
    1948           0 :     else                     ptxn_pool_idx_release( disp->ptxn_pool, EDGE_SPECIAL_IDX( txn_idx ) );
    1949           0 :   } else {
    1950           0 :     rtxn->in_degree = IN_DEGREE_ZOMBIE;
    1951           0 :     zombie_dlist_ele_push_tail( block->zombie_list, rtxn, disp->pool );
    1952           0 :     rtxn->block_idx = (uint)block_pool_idx( disp->block_pool, block );
    1953           0 :   }
    1954           0 : }
    1955             : 
    1956             : ulong
    1957             : fd_rdisp_staging_lane_info( fd_rdisp_t           const * disp,
    1958           0 :                             fd_rdisp_staging_lane_info_t out_sched[ static 4 ] ) {
    1959           0 :   for( ulong i=0UL; i<4UL; i++ ) {
    1960           0 :     if( !(disp->free_lanes & (1<<i) ) ) {
    1961           0 :       block_slist_t const * sl = disp->lanes[ i ].block_ll;
    1962           0 :       out_sched[ i ].insert_ready_block   = block_slist_ele_peek_tail( sl, disp->block_pool )->block;
    1963           0 :       out_sched[ i ].schedule_ready_block = block_slist_ele_peek_head( sl, disp->block_pool )->block;
    1964           0 :     }
    1965           0 :   }
    1966           0 :   return 0xFUL & ~(ulong)disp->free_lanes;
    1967           0 : }
    1968             : 
    1969             : void
    1970             : fd_rdisp_pseudo_txn_to_addr( fd_rdisp_t const * disp,
    1971             :                              ulong              txn_idx,
    1972           0 :                              fd_acct_addr_t   * out ) {
    1973           0 :   FD_TEST( txn_idx & FD_RDISP_LTHASH_PSEUDO_TXN );
    1974           0 :   txn_idx ^= FD_RDISP_LTHASH_PSEUDO_TXN;
    1975           0 :   fd_rdisp_ptxn_t const * ptxn = disp->ptxn_pool + txn_idx;
    1976             : 
    1977           0 :   if( FD_LIKELY( (ptxn->in_degree!=IN_DEGREE_UNSTAGED) & (ptxn->in_degree!=IN_DEGREE_UNSTAGED_DISPATCHED) ) ) {
    1978           0 :     *out = disp->acct_pool[ EDGE_SPECIAL_IDX(ptxn->last) ].key;
    1979           0 :   } else {
    1980           0 :     *out = disp->ustlt_pool[ EDGE_SPECIAL_IDX(ptxn->last) ].key.acct;
    1981           0 :   }
    1982           0 : }
    1983             : 
    1984             : void
    1985             : fd_rdisp_add_all_pseudo_txn( fd_rdisp_t          *  disp,
    1986           0 :                              FD_RDISP_BLOCK_TAG_T   insert_block ) {
    1987           0 :   while( fd_rdisp_add_pseudo_txn( disp, insert_block ) );
    1988           0 : }
    1989             : 
    1990             : /* Score of a staged pseudo-transaction.  It sequences behind every
    1991             :    real transaction of its serializing class. */
    1992             : static inline float
    1993           0 : ptxn_score( uint last_serializing ) {
    1994           0 :   return fd_rdisp_score( FD_RDISP_PTXN_SCORE, last_serializing );
    1995           0 : }
    1996             : 
    1997             : static ulong
    1998             : add_staged_pseudo_txn( fd_rdisp_t *           disp,
    1999           0 :                        fd_rdisp_blockinfo_t * block ) {
    2000           0 :   uint lane = block->staging_lane;
    2001           0 :   if( FD_UNLIKELY( !ptxn_pool_free( disp->ptxn_pool ) ) ) FD_LOG_CRIT(( "rdisp pseudo-transaction pool exhausted" ));
    2002           0 :   fd_rdisp_ptxn_t * ptxn = ptxn_pool_ele_acquire( disp->ptxn_pool );
    2003           0 :   uint ptxn_idx = EDGE_MAKE_SPECIAL(PTXN, (uint)(ptxn - disp->ptxn_pool));
    2004             : 
    2005           0 :   ulong acct_idx = fd_rdisp_mq_pop( disp->mq, lane );
    2006           0 :   acct_info_t * ai = disp->acct_pool + acct_idx;
    2007             : 
    2008           0 :   ptxn->in_degree = 0U;
    2009           0 :   if( FD_UNLIKELY( block->last_insert_was_serializing ) ) {
    2010           0 :     block->last_serializing = block->inserted_cnt;
    2011           0 :   }
    2012           0 :   block->last_insert_was_serializing = 0U;
    2013           0 :   ptxn->score = ptxn_score( block->last_serializing );
    2014             : 
    2015             :   /* see add_txn for this line */
    2016           0 :   ptxn->edge_cnt_etc = ((block->linear_block_number<<16) | (lane<<14)) - 1U;
    2017             : 
    2018             :   /* add_edges is more general than what we need, but it's better to
    2019             :      have all the logic in one place.  We don't need to pass new_wr
    2020             :      because it's only a reader, so it won't be accessed anyway. */
    2021           0 :   add_edges( disp, (fd_rdisp_txn_t *)fd_type_pun( ptxn ), &(ai->key), 1UL, lane, 0, 0, NULL, 0UL );
    2022             :   /* since we're kind of cheating with edge_cnt_etc, we need to fix it
    2023             :      here.  Additionally, type punning ptxn like that is a bit
    2024             :      fast-and-loose with the C spec, so add a compiler fence to make
    2025             :      sure these writes can't be reordered. */
    2026           0 :   FD_COMPILER_MFENCE();
    2027           0 :   ptxn->edge_cnt_etc += 1U<<7;
    2028           0 :   ptxn->last = EDGE_MAKE_SPECIAL(LAST, (uint)acct_idx);
    2029             : 
    2030             :   /* add_edge handles most of the state transitions, but it treats the
    2031             :      pseudo-transaction like a reader, so it needs a little fixing. */
    2032           0 :   uint states = ai->states;
    2033           0 :   states = fd_uint_if( ACCT_INFO_STATE_IS( states, lane, NEEDS_PTXN ), ACCT_INFO_STATE_SET( states, lane, NO_WRITERS ), states );
    2034           0 :   ai->states = (uchar)states;
    2035             : 
    2036           0 :   if( FD_LIKELY( ptxn->in_degree==0U ) ) {
    2037           0 :     pending_prq_ele_t temp[1] = {{ .score = ptxn->score, .linear_block_number = block->linear_block_number, .txn_idx = ptxn_idx }};
    2038           0 :     pending_prq_insert( disp->lanes[ lane ].pending, temp );
    2039           0 :   }
    2040           0 :   block->inserted_cnt++;
    2041           0 :   return ptxn_idx;
    2042           0 : }
    2043             : 
    2044             : ulong
    2045             : fd_rdisp_add_ready_pseudo_txn( fd_rdisp_t          *  disp,
    2046           0 :                                FD_RDISP_BLOCK_TAG_T   insert_block ) {
    2047           0 :   fd_rdisp_blockinfo_t * block = block_map_ele_query( disp->blockmap, &insert_block, NULL, disp->block_pool );
    2048           0 :   if( FD_UNLIKELY( !block || !block->insert_ready || !block->staged ) ) return 0UL;
    2049           0 :   if( FD_UNLIKELY( !fd_rdisp_mq_prio0_cnt( disp->mq, block->staging_lane ) ) ) return 0UL;
    2050           0 :   return add_staged_pseudo_txn( disp, block );
    2051           0 : }
    2052             : 
    2053             : ulong
    2054             : fd_rdisp_add_pseudo_txn( fd_rdisp_t          *  disp,
    2055           0 :                          FD_RDISP_BLOCK_TAG_T   insert_block ) {
    2056           0 :   fd_rdisp_blockinfo_t * block   = block_map_ele_query( disp->blockmap, &insert_block, NULL, disp->block_pool );
    2057           0 :   if( FD_UNLIKELY( !block || !block->insert_ready ) ) return 0UL;
    2058             : 
    2059           0 :   fd_rdisp_ptxn_t * ptxn;
    2060           0 :   uint ptxn_idx;
    2061             : 
    2062           0 :   if( FD_LIKELY( block->staged ) ) {
    2063           0 :     if( FD_UNLIKELY( !fd_rdisp_mq_cnt( disp->mq, block->staging_lane ) ) ) return 0UL;
    2064           0 :     return add_staged_pseudo_txn( disp, block );
    2065           0 :   } else {
    2066           0 :     if( FD_UNLIKELY( ustlt_dlist_is_empty( block->ustlt_needed, disp->ustlt_pool ) ) ) return 0UL;
    2067             : 
    2068           0 :     if( FD_UNLIKELY( !ptxn_pool_free( disp->ptxn_pool ) ) ) FD_LOG_CRIT(( "rdisp pseudo-transaction pool exhausted" ));
    2069           0 :     ptxn = ptxn_pool_ele_acquire( disp->ptxn_pool );
    2070           0 :     ptxn_idx = EDGE_MAKE_SPECIAL(PTXN, (uint)(ptxn - disp->ptxn_pool));
    2071             : 
    2072           0 :     ustlt_t * ustlt_ele = ustlt_dlist_ele_pop_head( block->ustlt_needed, disp->ustlt_pool );
    2073           0 :     ustlt_map_ele_remove_fast( disp->ustlt_map, ustlt_ele, disp->ustlt_pool );
    2074           0 :     ptxn->in_degree = IN_DEGREE_UNSTAGED;
    2075           0 :     ptxn->score = 0.0f;
    2076             : 
    2077           0 :     ptxn->edge_cnt_etc = 0U;
    2078             : 
    2079           0 :     ptxn->last          = (uint)(ustlt_ele - disp->ustlt_pool);
    2080           0 :     ustlt_ele->ptxn_idx = (uint)(ptxn      - disp->ptxn_pool );
    2081             : 
    2082           0 :     ustlt_dlist_ele_push_tail( block->ustlt_created, ustlt_ele, disp->ustlt_pool );
    2083           0 :   }
    2084           0 :   block->inserted_cnt++;
    2085           0 :   return ptxn_idx;
    2086           0 : }
    2087             : 
    2088             : int
    2089             : fd_rdisp_add_writable( fd_rdisp_t           * disp,
    2090             :                        FD_RDISP_BLOCK_TAG_T   insert_block,
    2091           0 :                        fd_acct_addr_t const * addr ) {
    2092           0 :   fd_rdisp_blockinfo_t * block   = block_map_ele_query( disp->blockmap, &insert_block, NULL, disp->block_pool );
    2093           0 :   if( FD_UNLIKELY( !block || !block->insert_ready ) ) return -1;
    2094             : 
    2095           0 :   if( FD_LIKELY( block->staged ) ) {
    2096           0 :     ulong idx  = lookup_pubkey( disp, addr );
    2097           0 :     ulong lane = block->staging_lane;
    2098           0 :     if( FD_LIKELY( fd_rdisp_mq_test( disp->mq, idx, block->staging_lane ) ) ) return 0;
    2099             : 
    2100           0 :     acct_info_t * ai = disp->acct_pool+idx;
    2101           0 :     uint states = ai->states;
    2102           0 :     states = fd_uint_if( ACCT_INFO_STATE_IS( states, lane, NO_WRITERS ), ACCT_INFO_STATE_SET( states, lane, NEEDS_PTXN ), states );
    2103           0 :     ai->states = (uchar)states;
    2104           0 :     if( FD_UNLIKELY( fd_rdisp_mq_cnt( disp->mq, lane )>=MAX_WRITERS_PER_BLOCK ) ) FD_LOG_CRIT(( "rdisp lane %lu writable account set full", lane ));
    2105           0 :     fd_rdisp_mq_insert( disp->mq, idx, lane, mq_prio( disp, ai, ACCT_INFO_STATE_IS( states, lane, NEEDS_PTXN ) ) );
    2106           0 :     return 1;
    2107           0 :   } else {
    2108           0 :     ustlt_key_t query[1] = {{
    2109           0 :       .block_idx = (uint)(block-disp->block_pool),
    2110           0 :       .acct      = *addr
    2111           0 :     }};
    2112           0 :     int new = (ustlt_map_idx_query( disp->ustlt_map, query, ULONG_MAX, disp->ustlt_pool )==ULONG_MAX);
    2113           0 :     if( FD_LIKELY( !new ) ) return 0;
    2114             : 
    2115           0 :     if( FD_UNLIKELY( ustlt_dlist_is_empty( disp->ustlt_free, disp->ustlt_pool ) ) ) FD_LOG_CRIT(( "rdisp unstaged LtHash entry pool exhausted" ));
    2116           0 :     ulong ustlt_idx = ustlt_dlist_idx_pop_head( disp->ustlt_free, disp->ustlt_pool );
    2117           0 :     disp->ustlt_pool[ ustlt_idx ].key = *query;
    2118           0 :     ustlt_map_idx_insert( disp->ustlt_map, ustlt_idx, disp->ustlt_pool );
    2119           0 :     ustlt_dlist_idx_push_tail( block->ustlt_needed, ustlt_idx, disp->ustlt_pool );
    2120           0 :     return 1;
    2121           0 :   }
    2122           0 : }
    2123             : 
    2124             : ulong
    2125             : fd_rdisp_add_extra_pseudo_txn( fd_rdisp_t           * disp,
    2126             :                                FD_RDISP_BLOCK_TAG_T   schedule_block,
    2127           0 :                                fd_acct_addr_t const * addr ) {
    2128           0 :   fd_rdisp_blockinfo_t * block   = block_map_ele_query( disp->blockmap, &schedule_block, NULL, disp->block_pool );
    2129           0 :   if( FD_UNLIKELY( !block || !block->schedule_ready ) ) return 0UL;
    2130             : 
    2131           0 :   uint ptxn_idx;
    2132             : 
    2133           0 :   if( FD_LIKELY( block->staged ) ) {
    2134           0 :     ulong idx  = lookup_pubkey( disp, addr );
    2135           0 :     uint  lane = block->staging_lane;
    2136             : 
    2137           0 :     acct_info_t * ai = disp->acct_pool+idx;
    2138           0 :     edge_t last = ai->last_reference[lane];
    2139           0 :     int insert_inline = 0;
    2140           0 :     if( FD_UNLIKELY( block->insert_ready ) ) {
    2141           0 :       insert_inline = 1;
    2142           0 :     } else if( FD_UNLIKELY( EDGE_IS_PTXN(last) ) ) {
    2143             :       /* If this is for the right block, we got lucky */
    2144           0 :       fd_rdisp_ptxn_t const * ptxn = disp->ptxn_pool + EDGE_SPECIAL_IDX(last);
    2145           0 :       if( FD_UNLIKELY( (ptxn->edge_cnt_etc>>16)==(0xFFFF&block->linear_block_number) ) ) return last;
    2146           0 :     } else if( FD_UNLIKELY( last==0U ) ) {
    2147           0 :       insert_inline=1;
    2148           0 :     } else {
    2149           0 :       fd_rdisp_txn_t const * txn = FOLLOW_EDGE_TXN( disp, last );
    2150           0 :       insert_inline = (txn->edge_cnt_etc>>16)==(0xFFFF&block->linear_block_number);
    2151           0 :     }
    2152             : 
    2153             :     /* If we haven't inserted an extra ptxn yet, and we're inserting
    2154             :        inline, treat this as a normal non-seralizing transaction.  ... */
    2155           0 :     if( FD_UNLIKELY( insert_inline && block->last_insert_was_serializing && !block->inserted_extra_ptxn ) ) {
    2156           0 :       block->last_serializing = block->inserted_cnt;
    2157           0 :       block->last_insert_was_serializing = 0U;
    2158           0 :     } else if( FD_UNLIKELY( !insert_inline && !block->inserted_extra_ptxn ) ) {
    2159             :       /* Otherwise, if this is the first non-inline one, bump
    2160             :          last_serializing once and don't touch it again.  The block
    2161             :          can't be insert-ready, so we won't insert any other
    2162             :          transactions that aren't from this function. */
    2163           0 :       block->last_serializing = block->inserted_cnt;
    2164           0 :       block->last_insert_was_serializing = 0U;
    2165           0 :       block->inserted_extra_ptxn = 1;
    2166           0 :     }
    2167             : 
    2168           0 :     if( FD_UNLIKELY( !ptxn_pool_free( disp->ptxn_pool ) ) ) FD_LOG_CRIT(( "rdisp pseudo-transaction pool exhausted" ));
    2169           0 :     fd_rdisp_ptxn_t * ptxn = ptxn_pool_ele_acquire( disp->ptxn_pool );
    2170           0 :     ptxn_idx = EDGE_MAKE_SPECIAL(PTXN, (uint)(ptxn - disp->ptxn_pool));
    2171           0 :     if( FD_LIKELY( insert_inline ) ) {
    2172             :       /* What follows below is essentially the same as the normal
    2173             :          add_pseudo_txn case */
    2174           0 :       ptxn->in_degree = 0U;
    2175           0 :       ptxn->score = ptxn_score( block->last_serializing );
    2176           0 :       ptxn->edge_cnt_etc = ((block->linear_block_number<<16) | (lane<<14)) - 1U;
    2177           0 :       add_edges( disp, (fd_rdisp_txn_t *)fd_type_pun( ptxn ), &(ai->key), 1UL, lane, 0, 0, NULL, 0UL );
    2178           0 :       FD_COMPILER_MFENCE();
    2179           0 :       ptxn->edge_cnt_etc += 1U<<7;
    2180           0 :       ptxn->last = EDGE_MAKE_SPECIAL(LAST, (uint)idx);
    2181             : 
    2182           0 :       uint states = ai->states;
    2183           0 :       states = fd_uint_if( ACCT_INFO_STATE_IS( states, lane, NEEDS_PTXN ), ACCT_INFO_STATE_SET( states, lane, NO_WRITERS ), states );
    2184           0 :       ai->states = (uchar)states;
    2185             : 
    2186           0 :       if( FD_LIKELY( ptxn->in_degree==0U ) ) {
    2187           0 :         pending_prq_ele_t temp[1] = {{ .score = ptxn->score, .linear_block_number = block->linear_block_number, .txn_idx = ptxn_idx }};
    2188           0 :         pending_prq_insert( disp->lanes[ block->staging_lane ].pending, temp );
    2189           0 :       }
    2190           0 :     } else {
    2191             :       /* Otherwise there's already a transaction for a later block in
    2192             :          the DAG for this account, we insert a hacky 0-edge pseudo-txn
    2193             :          and make use of the serializing functionality to make sure it
    2194             :          executes after all the transactions.  Since this block is not
    2195             :          insert-ready, we don't need to worry about preventing later
    2196             :          transactions from happening before this one. */
    2197             : 
    2198           0 :       ptxn->in_degree = 0U;
    2199           0 :       ptxn->edge_cnt_etc = ((block->linear_block_number<<16) | (lane<<14)); /* r_cnt==0, w_cnt is ignored */
    2200           0 :       ptxn->score = ptxn_score( block->last_serializing );
    2201           0 :       ptxn->last = EDGE_MAKE_SPECIAL(LAST, (uint)idx);
    2202             :       /* We know that there's a transaction that must execute after this
    2203             :          one in the DAG, so the account info can't get freed until after
    2204             :          this transaction has completed.  That means we don't need to
    2205             :          touch ai->states. */
    2206           0 :       pending_prq_ele_t temp[1] = {{ .score = ptxn->score, .linear_block_number = block->linear_block_number, .txn_idx = ptxn_idx }};
    2207           0 :       pending_prq_insert( disp->lanes[ block->staging_lane ].pending, temp );
    2208           0 :     }
    2209           0 :   } else {
    2210           0 :     if( FD_UNLIKELY( ustlt_dlist_is_empty( disp->ustlt_free, disp->ustlt_pool ) ) ) FD_LOG_CRIT(( "rdisp unstaged LtHash entry pool exhausted" ));
    2211           0 :     ulong ustlt_idx = ustlt_dlist_idx_pop_head( disp->ustlt_free, disp->ustlt_pool );
    2212           0 :     disp->ustlt_pool[ ustlt_idx ].key.block_idx = (uint)(block-disp->block_pool);
    2213           0 :     disp->ustlt_pool[ ustlt_idx ].key.acct      = *addr;
    2214             : 
    2215           0 :     if( FD_UNLIKELY( !ptxn_pool_free( disp->ptxn_pool ) ) ) FD_LOG_CRIT(( "rdisp pseudo-transaction pool exhausted" ));
    2216           0 :     fd_rdisp_ptxn_t * ptxn = ptxn_pool_ele_acquire( disp->ptxn_pool );
    2217           0 :     ptxn_idx = EDGE_MAKE_SPECIAL(PTXN, (uint)(ptxn - disp->ptxn_pool));
    2218             : 
    2219           0 :     ptxn->in_degree = IN_DEGREE_UNSTAGED;
    2220           0 :     ptxn->score = 0.0f;
    2221           0 :     ptxn->edge_cnt_etc = 0U;
    2222           0 :     ptxn->last = (uint)ustlt_idx;
    2223             : 
    2224           0 :     disp->ustlt_pool[ ustlt_idx ].ptxn_idx = (uint)(ptxn - disp->ptxn_pool);
    2225           0 :     ustlt_dlist_idx_push_tail( block->ustlt_created, ustlt_idx, disp->ustlt_pool );
    2226           0 :   }
    2227           0 :   block->inserted_cnt++;
    2228           0 :   return ptxn_idx;
    2229           0 : }
    2230             : 
    2231             : ulong
    2232             : fd_rdisp_lane_writable_cnt( fd_rdisp_t * disp,
    2233           0 :                             ulong        staging_lane ) {
    2234           0 :   FD_TEST( staging_lane<4UL );
    2235           0 :   return fd_rdisp_mq_cnt( disp->mq, staging_lane );
    2236           0 : }
    2237             : 
    2238             : ulong
    2239             : fd_rdisp_lane_ready_acct_cnt( fd_rdisp_t * disp,
    2240           0 :                               ulong        staging_lane ) {
    2241           0 :   return fd_rdisp_mq_prio0_cnt( disp->mq, staging_lane );
    2242           0 : }
    2243             : 
    2244             : void
    2245             : fd_rdisp_set_ready_acct_ema_max( fd_rdisp_t * disp,
    2246           0 :                                  float        ema_max ) {
    2247           0 :   disp->ready_acct_ema_max = ema_max;
    2248           0 : }
    2249             : 
    2250             : void
    2251             : fd_rdisp_verify( fd_rdisp_t const * disp,
    2252           0 :                  uint             * scratch ) {
    2253           0 :   ulong acct_depth  = disp->depth*FD_RDISP_MAX_ACCT_PER_TXN+disp->acct_extra;
    2254           0 :   ulong block_depth = disp->block_depth;
    2255           0 :   FD_TEST( 0==acct_map_verify ( disp->acct_map,  acct_depth +1UL, disp->acct_pool  ) );
    2256           0 :   FD_TEST( 0==block_map_verify( disp->blockmap,  block_depth+1UL, disp->block_pool ) );
    2257           0 :   FD_TEST( 0==ustlt_map_verify( disp->ustlt_map, acct_depth +1UL, disp->ustlt_pool ) );
    2258             : 
    2259             :   /* Every element of the free list that is in the map is CACHED under
    2260             :      its own key, every element in the map is either ACTIVE with a
    2261             :      reference in some lane or CACHED, and the CACHED ones are exactly
    2262             :      the mapped elements of the free list. */
    2263           0 :   ulong cached_cnt = 0UL;
    2264           0 :   for( free_dlist_iter_t it=free_dlist_iter_fwd_init( disp->free_acct_dlist, disp->acct_pool );
    2265           0 :        !free_dlist_iter_done( it, disp->free_acct_dlist, disp->acct_pool );
    2266           0 :        it=free_dlist_iter_fwd_next( it, disp->free_acct_dlist, disp->acct_pool ) ) {
    2267           0 :     ulong idx = free_dlist_iter_idx( it, disp->free_acct_dlist, disp->acct_pool );
    2268           0 :     acct_info_t const * ai = disp->acct_pool+idx;
    2269           0 :     if( !ai->next ) continue; /* FREE */
    2270           0 :     FD_TEST( ai->cached==ACCT_INFO_CACHED && !ai->states );
    2271           0 :     FD_TEST( acct_map_idx_query_const( disp->acct_map, &ai->key, ULONG_MAX, disp->acct_pool )==idx );
    2272           0 :     cached_cnt++;
    2273           0 :   }
    2274           0 :   for( acct_map_iter_t it=acct_map_iter_init( disp->acct_map, disp->acct_pool );
    2275           0 :        !acct_map_iter_done( it, disp->acct_map, disp->acct_pool );
    2276           0 :        it=acct_map_iter_next( it, disp->acct_map, disp->acct_pool ) ) {
    2277           0 :     acct_info_t const * ai = acct_map_iter_ele_const( it, disp->acct_map, disp->acct_pool );
    2278           0 :     if( ai->cached==ACCT_INFO_CACHED ) cached_cnt--;
    2279           0 :     else FD_TEST( ai->states | ai->last_reference[ 0 ] | ai->last_reference[ 1 ] | ai->last_reference[ 2 ] | ai->last_reference[ 3 ] );
    2280           0 :   }
    2281           0 :   FD_TEST( cached_cnt==0UL );
    2282             : 
    2283             :   /* Every unstaged LtHash entry is in the free list or in one list of
    2284             :      one unstaged block. */
    2285           0 :   ulong ustlt_cnt = 0UL;
    2286           0 :   for( ustlt_dlist_iter_t it=ustlt_dlist_iter_fwd_init( disp->ustlt_free, disp->ustlt_pool );
    2287           0 :        !ustlt_dlist_iter_done( it, disp->ustlt_free, disp->ustlt_pool );
    2288           0 :        it=ustlt_dlist_iter_fwd_next( it, disp->ustlt_free, disp->ustlt_pool ) ) ustlt_cnt++;
    2289           0 :   for( block_map_iter_t it=block_map_iter_init( disp->blockmap, disp->block_pool );
    2290           0 :        !block_map_iter_done( it, disp->blockmap, disp->block_pool );
    2291           0 :        it=block_map_iter_next( it, disp->blockmap, disp->block_pool ) ) {
    2292           0 :     fd_rdisp_blockinfo_t const * block = block_map_iter_ele_const( it, disp->blockmap, disp->block_pool );
    2293           0 :     if( block->staged ) continue;
    2294           0 :     ustlt_dlist_t const * lists[ 2 ] = { block->ustlt_needed, block->ustlt_created };
    2295           0 :     for( ulong l=0UL; l<2UL; l++ ) {
    2296           0 :       for( ustlt_dlist_iter_t jt=ustlt_dlist_iter_fwd_init( lists[ l ], disp->ustlt_pool );
    2297           0 :            !ustlt_dlist_iter_done( jt, lists[ l ], disp->ustlt_pool );
    2298           0 :            jt=ustlt_dlist_iter_fwd_next( jt, lists[ l ], disp->ustlt_pool ) ) {
    2299           0 :         FD_TEST( ustlt_dlist_iter_ele_const( jt, lists[ l ], disp->ustlt_pool )->key.block_idx==(uint)(block-disp->block_pool) );
    2300           0 :         ustlt_cnt++;
    2301           0 :       }
    2302           0 :     }
    2303           0 :   }
    2304           0 :   FD_TEST( ustlt_cnt==acct_depth );
    2305             : 
    2306             :   /* Check all the in degree counts are right */
    2307           0 :   memset( scratch, '\0', sizeof(uint)*((disp->depth+acct_depth+2UL)) );
    2308           0 :   scratch[ 0 ] = UINT_MAX;
    2309             : 
    2310           0 :   for( ulong j=0UL; j<(disp->depth+acct_depth+2UL); j++ ) {
    2311           0 :     int ptxn = j>disp->depth;
    2312           0 :     fd_rdisp_txn_t const * rtxn = !ptxn ? disp->pool+j : (fd_rdisp_txn_t const *)(disp->ptxn_pool+(j-disp->depth-1UL));
    2313           0 :     if( rtxn->in_degree==IN_DEGREE_FREE ) { scratch[ j ]=UINT_MAX; continue; }
    2314             : 
    2315           0 :     if( (rtxn->in_degree==IN_DEGREE_UNSTAGED_DISPATCHED) |
    2316           0 :         (rtxn->in_degree==IN_DEGREE_UNSTAGED)            |
    2317           0 :         (rtxn->in_degree==IN_DEGREE_ZOMBIE) ) continue;
    2318             : 
    2319           0 :     ulong w_cnt = fd_ulong_if( ptxn, 0UL, 1UL + ((rtxn->edge_cnt_etc    ) & 0x7FU) );
    2320           0 :     ulong r_cnt = (rtxn->edge_cnt_etc>> 7) & 0x7FU;
    2321           0 :     ulong edge_idx = 0UL;
    2322             : 
    2323           0 :     for( ulong i=0UL; i<w_cnt+r_cnt; i++ ) {
    2324           0 :       edge_t const * e = rtxn->edges+edge_idx;
    2325           0 :       edge_t const  e0 = *e;
    2326             : 
    2327           0 :       edge_idx += fd_ulong_if( i<w_cnt, 1UL, 3UL );
    2328             : 
    2329           0 :       if( FD_UNLIKELY( EDGE_IS_LAST( e0 ) ) ) {
    2330             :         /* Make sure acct is ACTIVE */
    2331           0 :         acct_info_t * ai = disp->acct_pool + EDGE_SPECIAL_IDX(e0);
    2332           0 :         FD_TEST( !( (ai->states==0)&
    2333           0 :                     (ai->last_reference[ 0 ]==0U)&(ai->last_reference[ 1 ]==0U)&
    2334           0 :                     (ai->last_reference[ 2 ]==0U)&(ai->last_reference[ 3 ]==0U) ) );
    2335           0 :         FD_TEST( EDGE_SPECIAL_IDX(e0)==acct_map_idx_query_const( disp->acct_map, &(ai->key), ULONG_MAX, disp->acct_pool ) );
    2336           0 :         continue;
    2337           0 :       }
    2338             : 
    2339           0 :       edge_t next_e = e0;
    2340           0 :       edge_t const * child_edge;
    2341           0 :       edge_t last_e = 0U;
    2342           0 :       while( 1 ) {
    2343           0 :         int child_is_writer;
    2344             :         /* This loop first traverses the me->child link, and then
    2345             :            traverses any sibling links.  For example, in the case that
    2346             :            we're completing node D above, the first child_txn is E,
    2347             :            and the second child_txn is F. */
    2348           0 :         child_edge = FOLLOW_EDGE(     disp, next_e, child_is_writer );
    2349             : 
    2350           0 :         scratch[ fd_ulong_if( EDGE_IS_PTXN(next_e), EDGE_SPECIAL_IDX(next_e)+disp->depth+1UL, next_e>>8 ) ]++;
    2351             : 
    2352           0 :         if( child_is_writer || child_edge[1]==e0 ) break;
    2353           0 :         if( last_e!=0U ) FD_TEST( child_edge[2]==last_e );
    2354           0 :         last_e = next_e;
    2355           0 :         next_e = child_edge[1];
    2356           0 :         FD_TEST( next_e>=0x100U );
    2357           0 :       }
    2358           0 :     }
    2359           0 :   }
    2360           0 :   for( ulong i=0UL; i<(disp->depth+acct_depth+2UL); i++ ) {
    2361           0 :     fd_rdisp_txn_t const * rtxn = i<=disp->depth ? disp->pool+i : (fd_rdisp_txn_t const *)(disp->ptxn_pool+(i-disp->depth-1UL));
    2362           0 :     FD_TEST( scratch[ i ]==UINT_MAX ||
    2363           0 :              rtxn->in_degree==IN_DEGREE_DISPATCHED ||
    2364           0 :              rtxn->in_degree==IN_DEGREE_UNSTAGED ||
    2365           0 :              rtxn->in_degree==IN_DEGREE_UNSTAGED_DISPATCHED ||
    2366           0 :              rtxn->in_degree==IN_DEGREE_ZOMBIE ||
    2367           0 :              rtxn->in_degree==scratch[ i ] );
    2368           0 :   }
    2369           0 : }
    2370             : 
    2371           0 : void * fd_rdisp_leave ( fd_rdisp_t * disp ) { return disp; }
    2372           0 : void * fd_rdisp_delete( void * mem        ) { return  mem; }

Generated by: LCOV version 1.14