LCOV - code coverage report
Current view: top level - disco/poh - fd_poh_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 388 0.0 %
Date: 2024-11-13 11:58:15 Functions: 0 19 0.0 %

          Line data    Source code
       1             : #include "fd_poh_tile.h"
       2             : 
       3             : #include "../metrics/fd_metrics.h"
       4             : 
       5             : ulong
       6           0 : fd_poh_tile_align( void ) {
       7           0 :   return 128UL;
       8           0 : }
       9             : 
      10             : ulong
      11           0 : fd_poh_tile_footprint( void ) {
      12           0 :   ulong l = FD_LAYOUT_INIT;
      13           0 :   l = FD_LAYOUT_APPEND( l, alignof( fd_poh_tile_ctx_t ), sizeof( fd_poh_tile_ctx_t ) );
      14           0 :   l = FD_LAYOUT_APPEND( l, fd_stake_ci_align(), fd_stake_ci_footprint() );
      15           0 :   l = FD_LAYOUT_APPEND( l, FD_SHA256_ALIGN, FD_SHA256_FOOTPRINT );
      16           0 :   return FD_LAYOUT_FINI( l, fd_poh_tile_align() );
      17           0 : }
      18             : 
      19             : void
      20             : fd_poh_tile_initialize( fd_poh_tile_ctx_t * ctx,
      21             :                         ulong               tick_duration_ns, /* See clock comments above, will be 500ns for mainnet-beta. */
      22             :                         ulong               hashcnt_per_tick,    /* See clock comments above, will be 12,500 for mainnet-beta. */
      23             :                         ulong               ticks_per_slot,      /* See clock comments above, will almost always be 64. */
      24             :                         ulong               tick_height,         /* The counter (height) of the tick to start hashing on top of. */
      25           0 :                         uchar const *       last_entry_hash      /* Points to start of a 32 byte region of memory, the hash itself at the tick height. */ ) {
      26           0 :   FD_LOG_WARNING(( "tick_duration_ns: %lu",tick_duration_ns ));
      27           0 :   ctx->slot                = (tick_height/ticks_per_slot)+1UL;
      28           0 :   ctx->hashcnt             = 0UL;
      29           0 :   ctx->last_slot           = ctx->slot;
      30           0 :   ctx->last_hashcnt        = 0UL;
      31           0 :   ctx->reset_slot          = ctx->slot;
      32           0 :   ctx->reset_slot_start_ns = fd_log_wallclock(); /* safe to call from Rust */
      33             : 
      34           0 :   memcpy( ctx->hash, last_entry_hash, 32UL );
      35             : 
      36             :   /* Static configuration about the clock. */
      37           0 :   ctx->tick_duration_ns = tick_duration_ns;
      38           0 :   ctx->hashcnt_per_tick = hashcnt_per_tick;
      39           0 :   ctx->ticks_per_slot   = ticks_per_slot;
      40             : 
      41             :   /* Recompute derived information about the clock. */
      42           0 :   ctx->slot_duration_ns    = (double)ticks_per_slot*(double)tick_duration_ns;
      43           0 :   ctx->hashcnt_duration_ns = (double)tick_duration_ns/(double)hashcnt_per_tick;
      44           0 :   ctx->hashcnt_per_slot    = ticks_per_slot*hashcnt_per_tick;
      45             : 
      46             :   /* Can be derived from other information, but we precompute it
      47             :      since it is used frequently. */
      48           0 :   ctx->hashcnt_per_slot = ticks_per_slot*hashcnt_per_tick;
      49             : 
      50           0 :   if( FD_UNLIKELY( ctx->hashcnt_per_tick==1UL ) ) {
      51             :     /* Low power producer, maximum of one microblock per tick in the slot */
      52           0 :     ctx->max_microblocks_per_slot = ctx->ticks_per_slot;
      53           0 :   } else {
      54             :     /* See the long comment in after_credit for this limit */
      55           0 :     ctx->max_microblocks_per_slot = fd_ulong_min( MAX_MICROBLOCKS_PER_SLOT, ctx->ticks_per_slot*(ctx->hashcnt_per_tick-1UL) );
      56           0 :   }
      57           0 : }
      58             : 
      59             : /* fd_poh_tile_reached_leader_slot returns 1 if we have reached a slot
      60             :    where we are leader.  This is used by the replay stage to determine
      61             :    if it should create a new leader bank descendant of the prior reset
      62             :    slot block.
      63             : 
      64             :    Sometimes, even when we reach our slot we do not return 1, as we are
      65             :    giving a grace period to the prior leader to finish publishing their
      66             :    block.
      67             : 
      68             :    out_leader_slot is the slot height of the leader slot we reached, and
      69             :    reset_slot is the slot height of the last good (unskipped) slot we
      70             :    are building on top of. */
      71             : 
      72             : int
      73             : fd_poh_tile_reached_leader_slot( fd_poh_tile_ctx_t * ctx,
      74             :                                  ulong *             out_leader_slot,
      75           0 :                                  ulong *             out_reset_slot ) {
      76           0 :   *out_leader_slot = ctx->next_leader_slot;
      77           0 :   *out_reset_slot  = ctx->reset_slot;
      78             : 
      79           0 :   if( FD_UNLIKELY( ctx->next_leader_slot==ULONG_MAX ||
      80           0 :                    ctx->slot<ctx->next_leader_slot ) ) {
      81             :     /* Didn't reach our leader slot yet. */
      82           0 :     return 0;
      83           0 :   }
      84             : 
      85           0 :   if( FD_LIKELY( ctx->reset_slot==ctx->next_leader_slot ) ) {
      86             :     /* We were reset onto our leader slot, because the prior leader
      87             :        completed theirs, so we should start immediately, no need for a
      88             :        grace period. */
      89           0 :     return 1;
      90           0 :   }
      91             : 
      92             :   // if( FD_LIKELY( ctx->next_leader_slot>=1UL ) ) {
      93             :   //   fd_epoch_leaders_t * leaders = fd_stake_ci_get_lsched_for_slot( ctx->stake_ci, ctx->next_leader_slot-1UL ); /* Safe to call from Rust */
      94             :   //   if( FD_LIKELY( leaders ) ) {
      95             :   //     fd_pubkey_t const * leader = fd_epoch_leaders_get( leaders, ctx->next_leader_slot-1UL ); /* Safe to call from Rust */
      96             :   //     if( FD_LIKELY( leader ) ) {
      97             :   //       if( FD_UNLIKELY( !memcmp( leader->uc, ctx->identity_key.uc, 32UL ) ) ) {
      98             :   //         /* We were the leader in the previous slot, so also no need for
      99             :   //           a grace period.  We wouldn't get here if we were still
     100             :   //           processing the prior slot so begin new one immediately. */
     101             :   //         return 1;
     102             :   //       }
     103             :   //     }
     104             :   //   }
     105             :   // }
     106             : 
     107           0 :   if( FD_UNLIKELY( ctx->next_leader_slot-ctx->reset_slot>=4UL ) ) {
     108             :     /* The prior leader has not completed any slot successfully during
     109             :        their 4 leader slots, so they are probably inactive and no need
     110             :        to give a grace period. */
     111           0 :     return 1;
     112           0 :   }
     113             : 
     114           0 :   if( FD_LIKELY( ctx->slot-ctx->next_leader_slot<GRACE_SLOTS ) ) {
     115             :     /*  The prior leader hasn't finished their last slot, and they are
     116             :         likely still publishing, and within their grace period of two
     117             :         slots so we will keep waiting. */
     118           0 :     return 0;
     119           0 :   }
     120             : 
     121           0 :   return 1;
     122           0 : }
     123             : 
     124             : void
     125             : fd_poh_tile_publish_became_leader( fd_poh_tile_ctx_t * ctx,
     126             :                                    void const *        current_leader_data,
     127           0 :                                    ulong               slot ) {
     128           0 :   double tick_per_ns = fd_tempo_tick_per_ns( NULL );
     129           0 :   fd_histf_sample( ctx->begin_leader_delay, (ulong)((double)(fd_log_wallclock()-ctx->reset_slot_start_ns)/tick_per_ns) );
     130             : 
     131           0 :   long slot_start_ns = ctx->reset_slot_start_ns + (long)((double)(slot-ctx->reset_slot)*ctx->slot_duration_ns);
     132             : 
     133             :   /* No need to check flow control, there are always credits became when we
     134             :      are leader, we will not "become" leader again until we are done, so at
     135             :      most one frag in flight at a time. */
     136             : 
     137           0 :   uchar * dst = ctx->get_pack_buffer_func( ctx->arg );
     138             : 
     139           0 :   fd_became_leader_t * leader = (fd_became_leader_t *)dst;
     140           0 :   leader->slot_start_ns           = slot_start_ns;
     141           0 :   leader->slot_end_ns             = (long)((double)slot_start_ns + ctx->slot_duration_ns);
     142           0 :   leader->bank                    = current_leader_data;
     143           0 :   leader->max_microblocks_in_slot = ctx->max_microblocks_per_slot;
     144           0 :   leader->ticks_per_slot          = ctx->ticks_per_slot;
     145           0 :   leader->total_skipped_ticks     = ctx->ticks_per_slot*(slot-ctx->reset_slot);
     146             : 
     147           0 :   if( FD_UNLIKELY( leader->ticks_per_slot+leader->total_skipped_ticks>=MAX_SKIPPED_TICKS ) )
     148           0 :     FD_LOG_ERR(( "Too many skipped ticks %lu for slot %lu, chain must halt", leader->ticks_per_slot+leader->total_skipped_ticks, slot ));
     149             : 
     150           0 :   FD_LOG_INFO(( "became_leader(slot=%lu)", slot ));
     151             :   
     152           0 :   ulong sig = fd_disco_poh_sig( slot, POH_PKT_TYPE_BECAME_LEADER, 0UL );
     153           0 :   ulong tspub = (ulong)fd_frag_meta_ts_comp( fd_tickcount() );
     154           0 :   ctx->publish_pack_func( ctx->arg, tspub, sig, sizeof(fd_became_leader_t) );
     155           0 : }
     156             : 
     157             : /* The PoH tile knows when it should become leader by waiting for its
     158             :    leader slot (with the operating system clock).  This function is so
     159             :    that when it becomes the leader, it can be told what the leader bank
     160             :    is by the replay stage.  See the notes in the long comment above for
     161             :    more on how this works. */
     162             : 
     163             : void
     164             : fd_poh_tile_begin_leader( fd_poh_tile_ctx_t * ctx,
     165             :                           ulong               slot,
     166           0 :                           ulong               hashcnt_per_tick ) {
     167           0 :   FD_TEST( ctx->current_leader_slot == FD_SLOT_NULL );
     168             : 
     169           0 :   if( FD_UNLIKELY( slot!=ctx->slot ) ) FD_LOG_ERR(( "Trying to begin leader slot %lu but we are now on slot %lu", slot, ctx->slot ));
     170           0 :   if( FD_UNLIKELY( slot!=ctx->next_leader_slot ) ) FD_LOG_ERR(( "Trying to begin leader slot %lu but we are now on slot %lu", slot, ctx->next_leader_slot ));
     171             : 
     172           0 :   if( FD_UNLIKELY( ctx->hashcnt_per_tick!=hashcnt_per_tick ) ) {
     173           0 :     FD_LOG_WARNING(( "hashes per tick changed from %lu to %lu", ctx->hashcnt_per_tick, hashcnt_per_tick ));
     174             : 
     175             :     /* Recompute derived information about the clock. */
     176           0 :     ctx->hashcnt_duration_ns = (double)ctx->tick_duration_ns/(double)hashcnt_per_tick;
     177           0 :     ctx->hashcnt_per_slot = ctx->ticks_per_slot*hashcnt_per_tick;
     178           0 :     ctx->hashcnt_per_tick = hashcnt_per_tick;
     179             : 
     180           0 :     if( FD_UNLIKELY( ctx->hashcnt_per_tick==1UL ) ) {
     181             :       /* Low power producer, maximum of one microblock per tick in the slot */
     182           0 :       ctx->max_microblocks_per_slot = ctx->ticks_per_slot;
     183           0 :     } else {
     184             :       /* See the long comment in after_credit for this limit */
     185           0 :       ctx->max_microblocks_per_slot = fd_ulong_min( MAX_MICROBLOCKS_PER_SLOT, ctx->ticks_per_slot*(ctx->hashcnt_per_tick-1UL) );
     186           0 :     }
     187             : 
     188             :     /* Discard any ticks we might have done in the interim.  They will
     189             :        have the wrong number of hashes per tick.  We can just catch back
     190             :        up quickly if not too many slots were skipped and hopefully
     191             :        publish on time.  Note that tick production and verification of
     192             :        skipped slots is done for the eventual bank that publishes a
     193             :        slot, for example:
     194             : 
     195             :         Reset Slot:            998
     196             :         Epoch Transition Slot: 1000
     197             :         Leader Slot:           1002
     198             : 
     199             :        In this case, if a feature changing the hashcnt_per_tick is
     200             :        activated in slot 1000, and we are publishing empty ticks for
     201             :        slots 998, 999, 1000, and 1001, they should all have the new
     202             :        hashes_per_tick number of hashes, rather than the older one, or
     203             :        some combination. */
     204             : 
     205           0 :     FD_TEST( ctx->last_slot==ctx->reset_slot );
     206           0 :     FD_TEST( !ctx->last_hashcnt );
     207           0 :     ctx->slot = ctx->reset_slot;
     208           0 :     FD_LOG_WARNING(("BEGIN SLOT: %lu", ctx->slot));
     209           0 :     ctx->hashcnt = 0UL;
     210           0 :   }
     211             : 
     212           0 :   ctx->current_leader_slot     = slot;
     213           0 :   ctx->microblocks_lower_bound = 0UL;
     214             : 
     215             :   /* We are about to start publishing to the shred tile for this slot
     216             :     so update the highwater mark so we never republish in this slot
     217             :     again.  Also check that the leader slot is greater than the
     218             :     highwater, which should have been ensured earlier. */
     219             : 
     220           0 :   FD_TEST( ctx->highwater_leader_slot==ULONG_MAX || slot>=ctx->highwater_leader_slot );
     221           0 :   ctx->highwater_leader_slot = fd_ulong_max( fd_ulong_if( ctx->highwater_leader_slot==ULONG_MAX, 0UL, ctx->highwater_leader_slot ), slot );
     222             : 
     223           0 :   fd_poh_tile_publish_became_leader( ctx, (void *)(ctx->reset_slot-1UL), slot );
     224           0 : }
     225             : 
     226             : /* Determine what the next slot is in the leader schedule is that we are
     227             :    leader.  Includes the current slot.  If we are not leader in what
     228             :    remains of the current and next epoch, return ULONG_MAX. */
     229             : 
     230             : ulong
     231           0 : fd_poh_tile_next_leader_slot( fd_poh_tile_ctx_t * ctx ) {
     232             :   /* If we have published anything in a particular slot, then we
     233             :      should never become leader for that slot again. */
     234           0 :   ulong min_leader_slot = fd_ulong_max( ctx->slot, fd_ulong_if( ctx->highwater_leader_slot==ULONG_MAX, 0UL, ctx->highwater_leader_slot ) );
     235             : 
     236           0 :   for(;;) {
     237           0 :     fd_epoch_leaders_t * leaders = fd_stake_ci_get_lsched_for_slot( ctx->stake_ci, min_leader_slot ); /* Safe to call from Rust */
     238           0 :     if( FD_UNLIKELY( !leaders ) ) break;
     239             : 
     240           0 :     while( min_leader_slot<(leaders->slot0+leaders->slot_cnt) ) {
     241           0 :       fd_pubkey_t const * leader = fd_epoch_leaders_get( leaders, min_leader_slot ); /* Safe to call from Rust */
     242           0 :       if( FD_UNLIKELY( !memcmp( leader->key, ctx->identity_key.key, 32UL ) ) ) return min_leader_slot;
     243           0 :       min_leader_slot++;
     244           0 :     }
     245           0 :   }
     246             : 
     247           0 :   return ULONG_MAX;
     248           0 : }
     249             : 
     250             : void
     251           0 : fd_poh_tile_no_longer_leader( fd_poh_tile_ctx_t * ctx ) {
     252             :   /* If we stop being leader in a slot, we can never become leader in
     253             :     that slot again, and all in-flight microblocks for that slot
     254             :     should be dropped. */
     255           0 :   ctx->highwater_leader_slot = fd_ulong_max( fd_ulong_if( ctx->highwater_leader_slot==ULONG_MAX, 0UL, ctx->highwater_leader_slot ), ctx->slot );
     256           0 :   ctx->current_leader_slot = FD_SLOT_NULL;
     257           0 :   ctx->next_leader_slot = fd_poh_tile_next_leader_slot( ctx );
     258             : 
     259           0 :   FD_COMPILER_MFENCE();
     260           0 :   ctx->signal_leader_change_func( ctx->arg );
     261           0 :   FD_LOG_INFO(( "fd_poh_tile_no_longer_leader(next_leader_slot=%lu)", ctx->next_leader_slot ));
     262           0 : }
     263             : 
     264             : void
     265             : fd_poh_tile_reset( fd_poh_tile_ctx_t * ctx,
     266             :                    ulong               completed_bank_slot, /* The slot that successfully produced a block */
     267             :                    uchar const *       reset_blockhash,     /* The hash of the last tick in the produced block */
     268           0 :                    ulong               hashcnt_per_tick     /* The hashcnt per tick of the bank that completed */) {
     269           0 :   int leader_before_reset = ctx->slot>=ctx->next_leader_slot;
     270           0 :   if( FD_UNLIKELY( leader_before_reset && ctx->current_leader_slot!=FD_SLOT_NULL ) ) {
     271             :   /* If we were in the middle of a leader slot that we notified pack
     272             :        pack to start packing for we can never publish into that slot
     273             :        again, mark all in-flight microblocks to be dropped. */
     274           0 :     ctx->highwater_leader_slot = fd_ulong_max( fd_ulong_if( ctx->highwater_leader_slot==ULONG_MAX, 0UL, ctx->highwater_leader_slot ), 1UL+ctx->slot );
     275           0 :   }
     276             : 
     277           0 :   if( FD_UNLIKELY( ctx->expect_sequential_leader_slot==(completed_bank_slot+1UL) ) ) {
     278             :     /* If we are being reset onto a slot, it means some block was fully
     279             :       processed, so we reset to build on top of it.  Typically we want
     280             :       to update the reset_slot_start_ns to the current time, because
     281             :       the network will give the next leader 400ms to publish,
     282             :       regardless of how long the prior leader took.
     283             : 
     284             :       But: if we were leader in the prior slot, and the block was our
     285             :       own we can do better.  We know that the next slot should start
     286             :       exactly 400ms after the prior one started, so we can use that as
     287             :       the reset slot start time instead. */
     288           0 :     ctx->reset_slot_start_ns = ctx->reset_slot_start_ns + (long)((double)((completed_bank_slot+1UL)-ctx->reset_slot)*ctx->slot_duration_ns);
     289           0 :   } else {
     290           0 :     ctx->reset_slot_start_ns = fd_log_wallclock(); /* safe to call from Rust */
     291           0 :   }
     292           0 :   ctx->expect_sequential_leader_slot = ULONG_MAX;  
     293             : 
     294           0 :   memcpy( ctx->hash, reset_blockhash, 32UL );
     295           0 :   ctx->slot         = completed_bank_slot+1UL;
     296           0 :   FD_LOG_WARNING(("RESET SLOT: %lu", ctx->slot));
     297           0 :   ctx->hashcnt      = 0UL;
     298           0 :   ctx->last_slot    = ctx->slot;
     299           0 :   ctx->last_hashcnt = 0UL;
     300           0 :   ctx->reset_slot   = ctx->slot;
     301             : 
     302           0 :   if( FD_UNLIKELY( ctx->hashcnt_per_tick!=hashcnt_per_tick ) ) {
     303           0 :     FD_LOG_WARNING(( "hashes per tick changed from %lu to %lu", ctx->hashcnt_per_tick, hashcnt_per_tick ));
     304             : 
     305             :     /* Recompute derived information about the clock. */
     306           0 :     ctx->hashcnt_duration_ns = (double)ctx->tick_duration_ns/(double)hashcnt_per_tick;
     307           0 :     ctx->hashcnt_per_slot = ctx->ticks_per_slot*hashcnt_per_tick;
     308           0 :     ctx->hashcnt_per_tick = hashcnt_per_tick;
     309             : 
     310           0 :     if( FD_UNLIKELY( ctx->hashcnt_per_tick==1UL ) ) {
     311             :       /* Low power producer, maximum of one microblock per tick in the slot */
     312           0 :       ctx->max_microblocks_per_slot = ctx->ticks_per_slot;
     313           0 :     } else {
     314             :       /* See the long comment in after_credit for this limit */
     315           0 :       ctx->max_microblocks_per_slot = fd_ulong_min( MAX_MICROBLOCKS_PER_SLOT, ctx->ticks_per_slot*(ctx->hashcnt_per_tick-1UL) );
     316           0 :     }
     317           0 :   }
     318             : 
     319           0 :   if( FD_UNLIKELY( leader_before_reset ) ) {
     320             :     /* No longer have a leader bank if we are reset. Replay stage will
     321             :        call back again to give us a new one if we should become leader
     322             :        for the reset slot.
     323             : 
     324             :        The order is important here, ctx->hashcnt must be updated before
     325             :        calling no_longer_leader. */
     326           0 :     fd_poh_tile_no_longer_leader( ctx );
     327           0 :   }
     328           0 :   ctx->next_leader_slot = fd_poh_tile_next_leader_slot( ctx );
     329           0 :   FD_LOG_INFO(( "fd_poh_tile_reset(slot=%lu,next_leader_slot=%lu) slots_until_leader=%lu", ctx->reset_slot, ctx->next_leader_slot, ctx->next_leader_slot-ctx->reset_slot ));
     330           0 : }
     331             : 
     332             : int
     333             : fd_poh_tile_get_leader_after_n_slots( fd_poh_tile_ctx_t * ctx,
     334             :                                       ulong               n,
     335           0 :                                       uchar               out_pubkey[ static 32 ] ) {
     336           0 :   ulong slot = ctx->slot + n;
     337           0 :   fd_epoch_leaders_t * leaders = fd_stake_ci_get_lsched_for_slot( ctx->stake_ci, slot ); /* Safe to call from Rust */
     338             : 
     339           0 :   int copied = 0;
     340           0 :   if( FD_LIKELY( leaders ) ) {
     341           0 :     fd_pubkey_t const * leader = fd_epoch_leaders_get( leaders, slot ); /* Safe to call from Rust */
     342           0 :     if( FD_LIKELY( leader ) ) {
     343           0 :       memcpy( out_pubkey, leader, 32UL );
     344           0 :       copied = 1;
     345           0 :     }
     346           0 :   }
     347           0 :   return copied;
     348           0 : }
     349             : 
     350             : void
     351             : fd_poh_tile_publish_tick( fd_poh_tile_ctx_t * ctx,
     352             :                           uchar               hash[ static 32 ],
     353           0 :                           int                 is_skipped ) {
     354           0 :   ulong hashcnt = ctx->hashcnt_per_tick*(1UL+(ctx->last_hashcnt/ctx->hashcnt_per_tick));
     355             : 
     356           0 :   uchar * dst = ctx->get_microblock_buffer_func( ctx->arg );
     357             : 
     358           0 :   FD_TEST( ctx->last_slot>=ctx->reset_slot );
     359           0 :   fd_entry_batch_meta_t * meta = (fd_entry_batch_meta_t *)dst;
     360           0 :   if( is_skipped ) {
     361           0 :     meta->reference_tick = 0UL;
     362           0 :     meta->block_complete = 0;
     363           0 :   } else {
     364           0 :     meta->reference_tick = hashcnt/ctx->hashcnt_per_tick;
     365           0 :     meta->block_complete = hashcnt==ctx->hashcnt_per_slot;
     366           0 :   }
     367           0 :   ulong slot = fd_ulong_if( meta->block_complete, ctx->slot-1UL, ctx->slot );
     368           0 :   meta->parent_offset = 1UL+slot-ctx->reset_slot;
     369             : 
     370           0 :   FD_TEST( hashcnt>ctx->last_hashcnt );
     371           0 :   ulong hash_delta = hashcnt-ctx->last_hashcnt;
     372             : 
     373           0 :   dst += sizeof(fd_entry_batch_meta_t);
     374           0 :   fd_entry_batch_header_t * tick = (fd_entry_batch_header_t *)dst;
     375           0 :   tick->hashcnt_delta = hash_delta;
     376           0 :   fd_memcpy( tick->hash, hash, 32UL );
     377           0 :   tick->txn_cnt = 0UL;
     378             : 
     379             :   // FD_LOG_WARNING(("PUB TICK(%d): %lu %lu %lu %lu", is_skipped, slot, hash_delta, hashcnt, ctx->hashcnt_per_slot ));
     380           0 :   ulong tspub = (ulong)fd_frag_meta_ts_comp( fd_tickcount() );
     381           0 :   ulong sz = sizeof(fd_entry_batch_meta_t)+sizeof(fd_entry_batch_header_t);
     382           0 :   ulong sig = fd_disco_poh_sig( slot, POH_PKT_TYPE_MICROBLOCK, 0UL );
     383           0 :   ctx->publish_microblock_func( ctx->arg, tspub, sig, sz );
     384             : 
     385           0 :   if( FD_UNLIKELY( hashcnt==ctx->hashcnt_per_slot ) ) {
     386           0 :     ctx->last_slot++;
     387           0 :     ctx->last_hashcnt = 0UL;
     388           0 :   } else {
     389           0 :     ctx->last_hashcnt = hashcnt;
     390           0 :   }
     391           0 : }
     392             : 
     393             : int
     394             : fd_poh_tile_after_credit( fd_poh_tile_ctx_t * ctx,
     395           0 :                           int *               opt_poll_in ) {
     396           0 :   int is_leader = ctx->next_leader_slot!=ULONG_MAX && ctx->slot>=ctx->next_leader_slot;
     397           0 :   if( FD_UNLIKELY( is_leader && ctx->current_leader_slot==FD_SLOT_NULL ) ) {
     398             :     /* If we are the leader, but we didn't yet learn what the leader
     399             :        bank object is from the replay stage, do not do any hashing.
     400             : 
     401             :        This is not ideal, but greatly simplifies the control flow. */
     402           0 :     return 0;
     403           0 :   }
     404             : 
     405             :   /* If we have skipped ticks pending because we skipped some slots to
     406             :      become leader, register them now one at a time. */
     407           0 :   if( FD_UNLIKELY( is_leader && ctx->last_slot<ctx->slot ) ) {
     408           0 :     ulong publish_hashcnt = ctx->last_hashcnt+ctx->hashcnt_per_tick;
     409           0 :     ulong tick_idx = (ctx->last_slot*ctx->ticks_per_slot+publish_hashcnt/ctx->hashcnt_per_tick)%MAX_SKIPPED_TICKS;
     410             : 
     411           0 :     ctx->register_tick_func( ctx->arg, ctx->current_leader_slot, ctx->skipped_tick_hashes[ tick_idx ] );
     412           0 :     fd_poh_tile_publish_tick( ctx, ctx->skipped_tick_hashes[ tick_idx ], 1 );
     413             : 
     414             :     /* If we are catching up now and publishing a bunch of skipped
     415             :        ticks, we do not want to process any incoming microblocks until
     416             :        all the skipped ticks have been published out; otherwise we would
     417             :        intersperse skipped tick messages with microblocks. */
     418           0 :     *opt_poll_in = 0;
     419           0 :     return 1;
     420           0 :   }
     421             : 
     422             : 
     423           0 :   int low_power_mode = ctx->hashcnt_per_tick==1UL;
     424             : 
     425             :   /* If we are the leader, always leave enough capacity in the slot so
     426             :      that we can mixin any potential microblocks still coming from the
     427             :      pack tile for this slot. */
     428           0 :   ulong max_remaining_microblocks = ctx->max_microblocks_per_slot - ctx->microblocks_lower_bound;
     429             :   /* With hashcnt_per_tick hashes per tick, we actually get
     430             :      hashcnt_per_tick-1 chances to mixin a microblock.  For each tick
     431             :      span that we need to reserve, we also need to reserve the hashcnt
     432             :      for the tick, hence the +
     433             :      max_remaining_microblocks/(hashcnt_per_tick-1) rounded up.
     434             : 
     435             :      However, if hashcnt_per_tick is 1 because we're in low power mode,
     436             :      this should probably just be max_remaining_microblocks. */
     437           0 :   ulong max_remaining_ticks_or_microblocks = max_remaining_microblocks;
     438           0 :   if( FD_LIKELY( !low_power_mode ) ) max_remaining_ticks_or_microblocks += (max_remaining_microblocks+ctx->hashcnt_per_tick-2UL)/(ctx->hashcnt_per_tick-1UL);
     439             : 
     440           0 :   ulong restricted_hashcnt = fd_ulong_if( ctx->hashcnt_per_slot>=max_remaining_ticks_or_microblocks, ctx->hashcnt_per_slot-max_remaining_ticks_or_microblocks, 0UL );
     441             : 
     442           0 :   ulong min_hashcnt = ctx->hashcnt;
     443             : 
     444           0 :   if( FD_LIKELY( !low_power_mode ) ) {
     445             :     /* Recall that there are two kinds of events that will get published
     446             :        to the shredder,
     447             : 
     448             :          (a) Ticks. These occur every 62,500 (hashcnt_per_tick) hashcnts,
     449             :              and there will be 64 (ticks_per_slot) of them in each slot.
     450             : 
     451             :              Ticks must not have any transactions mixed into the hash.
     452             :              This is not strictly needed in theory, but is required by the
     453             :              current consensus protocol.  They get published here in
     454             :              after_credit.
     455             : 
     456             :          (b) Microblocks.  These can occur at any other hashcnt, as long
     457             :              as it is not a tick.  Microblocks cannot be empty, and must
     458             :              have at least one transactions mixed in.  These get
     459             :              published in after_frag.
     460             : 
     461             :        If hashcnt_per_tick is 1, then we are in low power mode and the
     462             :        following does not apply, since we can mix in transactions at any
     463             :        time.
     464             : 
     465             :        In the normal, non-low-power mode, though, we have to be careful
     466             :        to make sure that we do not publish microblocks on tick
     467             :        boundaries.  To do that, we need to obey two rules:
     468             :          (i)  after_credit must not leave hashcnt one before a tick
     469             :               boundary
     470             :          (ii) if after_credit begins one before a tick boundary, it must
     471             :               advance hashcnt and publish the tick
     472             : 
     473             :        There's some interplay between min_hashcnt and restricted_hashcnt
     474             :        here, and we need to show that there's always a value of
     475             :        target_hashcnt we can pick such that
     476             :            min_hashcnt <= target_hashcnt <= restricted_hashcnt.
     477             :        We'll prove this by induction for current_slot==0 and
     478             :        is_leader==true, since all other slots should be the same.
     479             : 
     480             :        Let m_j and r_j be the min_hashcnt and restricted_hashcnt
     481             :        (respectively) for the jth call to after_credit in a slot.  We
     482             :        want to show that for all values of j, it's possible to pick a
     483             :        value h_j, the value of target_hashcnt for the jth call to
     484             :        after_credit (which is also the value of hashcnt after
     485             :        after_credit has completed) such that m_j<=h_j<=r_j.
     486             : 
     487             :        Additionally, let T be hashcnt_per_tick and N be ticks_per_slot.
     488             : 
     489             :        Starting with the base case, j==0.  m_j=0, and
     490             :          r_0 = N*T - max_microblocks_per_slot
     491             :                    - ceil(max_microblocks_per_slot/(T-1)).
     492             : 
     493             :        This is monotonic decreasing in max_microblocks_per_slot, so it
     494             :        achieves its minimum when max_microblocks_per_slot is its
     495             :        maximum.
     496             :            r_0 >= N*T - N*(T-1) - ceil( (N*(T-1))/(T-1))
     497             :                 = N*T - N*(T-1)-N = 0.
     498             :        Thus, m_0 <= r_0, as desired.
     499             : 
     500             : 
     501             : 
     502             :        Then, for the inductive step, assume there exists h_j such that
     503             :        m_j<=h_j<=r_j, and we want to show that there exists h_{j+1},
     504             :        which is the same as showing m_{j+1}<=r_{j+1}.
     505             : 
     506             :        Let a_j be 1 if we had a microblock immediately following the jth
     507             :        call to after_credit, and 0 otherwise.  Then hashcnt at the start
     508             :        of the (j+1)th call to after_frag is h_j+a_j.
     509             :        Also, set b_{j+1}=1 if we are in the case covered by rule (ii)
     510             :        above during the (j+1)th call to after_credit, i.e. if
     511             :        (h_j+a_j)%T==T-1.  Thus, m_{j+1} = h_j + a_j + b_{j+1}.
     512             : 
     513             :        If we received an additional microblock, then
     514             :        max_remaining_microblocks goes down by 1, and
     515             :        max_remaining_ticks_or_microblocks goes down by either 1 or 2,
     516             :        which means restricted_hashcnt goes up by either 1 or 2.  In
     517             :        particular, it goes up by 2 if the new value of
     518             :        max_remaining_microblocks (at the start of the (j+1)th call to
     519             :        after_credit) is congruent to 0 mod T-1.  Let b'_{j+1} be 1 if
     520             :        this condition is met and 0 otherwise.  If we receive a
     521             :        done_packing message, restricted_hashcnt can go up by more, but
     522             :        we can ignore that case, since it is less restrictive.
     523             :        Thus, r_{j+1}=r_j+a_j+b'_{j+1}.
     524             : 
     525             :        If h_j < r_j (strictly less), then h_j+a_j < r_j+a_j.  And thus,
     526             :        since b_{j+1}<=b'_{j+1}+1, just by virtue of them both being
     527             :        binary,
     528             :              h_j + a_j + b_{j+1} <  r_j + a_j + b'_{j+1} + 1,
     529             :        which is the same (for integers) as
     530             :              h_j + a_j + b_{j+1} <= r_j + a_j + b'_{j+1},
     531             :                  m_{j+1}         <= r_{j+1}
     532             : 
     533             :        On the other hand, if h_j==r_j, this is easy unless b_{j+1}==1,
     534             :        which can also only happen if a_j==1.  Then (h_j+a_j)%T==T-1,
     535             :        which means there's an integer k such that
     536             : 
     537             :              h_j+a_j==(ticks_per_slot-k)*T-1
     538             :              h_j    ==ticks_per_slot*T -  k*(T-1)-1  - k-1
     539             :                     ==ticks_per_slot*T - (k*(T-1)+1) - ceil( (k*(T-1)+1)/(T-1) )
     540             : 
     541             :        Since h_j==r_j in this case, and
     542             :        r_j==(ticks_per_slot*T) - max_remaining_microblocks_j - ceil(max_remaining_microblocks_j/(T-1)),
     543             :        we can see that the value of max_remaining_microblocks at the
     544             :        start of the jth call to after_credit is k*(T-1)+1.  Again, since
     545             :        a_j==1, then the value of max_remaining_microblocks at the start
     546             :        of the j+1th call to after_credit decreases by 1 to k*(T-1),
     547             :        which means b'_{j+1}=1.
     548             : 
     549             :        Thus, h_j + a_j + b_{j+1} == r_j + a_j + b'_{j+1}, so, in
     550             :        particular, h_{j+1}<=r_{j+1} as desired. */
     551           0 :      min_hashcnt += (ulong)(min_hashcnt%ctx->hashcnt_per_tick == (ctx->hashcnt_per_tick-1UL)); /* add b_{j+1}, enforcing rule (ii) */
     552           0 :   }
     553             :   /* Now figure out how many hashes are needed to "catch up" the hash
     554             :      count to the current system clock, and clamp it to the allowed
     555             :      range. */
     556           0 :   long now = fd_log_wallclock();
     557           0 :   ulong target_hashcnt = (ulong)((double)(now - ctx->reset_slot_start_ns) / ctx->hashcnt_duration_ns) - (ctx->slot-ctx->reset_slot)*ctx->hashcnt_per_slot;
     558             :   /* Clamp to [min_hashcnt, restricted_hashcnt] as above */
     559           0 :   target_hashcnt = fd_ulong_max( fd_ulong_min( target_hashcnt, restricted_hashcnt ), min_hashcnt );
     560             : 
     561             :   /* The above proof showed that it was always possible to pick a value
     562             :      of target_hashcnt, but we still have a lot of freedom in how to
     563             :      pick it.  It simplifies the code a lot if we don't keep going after
     564             :      a tick in this function.  In particular, we want to publish at most
     565             :      1 tick in this call, since otherwise we could consume infinite
     566             :      credits to publish here.  The credits are set so that we should
     567             :      only ever publish one tick during this loop.  Also, all the extra
     568             :      stuff (leader transitions, publishing ticks, etc.) we have to do
     569             :      happens at tick boundaries, so this lets us consolidate all those
     570             :      cases.
     571             : 
     572             :      Mathematically, since the current value of hashcnt is h_j+a_j, the
     573             :      next tick (advancing a full tick if we're currently at a tick) is
     574             :      t_{j+1} = T*(floor( (h_j+a_j)/T )+1).  We need to show that if we set
     575             :      h'_{j+1} = min( h_{j+1}, t_{j+1} ), it is still valid.
     576             : 
     577             :      First, h'_{j+1} <= h_{j+1} <= r_{j+1}, so we're okay in that
     578             :      direction.
     579             : 
     580             :      Next, observe that t_{j+1}>=h_j + a_j + 1, and recall that b_{j+1}
     581             :      is 0 or 1. So then,
     582             :                     t_{j+1} >= h_j+a_j+b_{j+1} = m_{j+1}.
     583             : 
     584             :      We know h_{j+1) >= m_{j+1} from before, so then h'_{j+1} >=
     585             :      m_{j+1}, as desired. */
     586             : 
     587           0 :   ulong next_tick_hashcnt = ctx->hashcnt_per_tick * (1UL+(ctx->hashcnt/ctx->hashcnt_per_tick));
     588           0 :   target_hashcnt = fd_ulong_min( target_hashcnt, next_tick_hashcnt );
     589             : 
     590             :   /* We still need to enforce rule (i). We know that min_hashcnt%T !=
     591             :      T-1 because of rule (ii).  That means that if target_hashcnt%T ==
     592             :      T-1 at this point, target_hashcnt > min_hashcnt (notice the
     593             :      strict), so target_hashcnt-1 >= min_hashcnt and is thus still a
     594             :      valid choice for target_hashcnt. */
     595           0 :   target_hashcnt -= (ulong)( (!low_power_mode) & ((target_hashcnt%ctx->hashcnt_per_tick)==(ctx->hashcnt_per_tick-1UL)) );
     596             : 
     597           0 :   FD_TEST( target_hashcnt >= ctx->hashcnt       );
     598           0 :   FD_TEST( target_hashcnt >= min_hashcnt        );
     599           0 :   FD_TEST( target_hashcnt <= restricted_hashcnt );
     600             : 
     601           0 :   if( FD_UNLIKELY( ctx->hashcnt==target_hashcnt ) ) return 0; /* Nothing to do, don't publish a tick twice */
     602             : 
     603           0 :   while( ctx->hashcnt<target_hashcnt ) {
     604           0 :     fd_sha256_hash( ctx->hash, 32UL, ctx->hash );
     605           0 :     ctx->hashcnt++;
     606           0 :   }
     607             : 
     608           0 :   if( FD_UNLIKELY( ctx->hashcnt==ctx->hashcnt_per_slot ) ) {
     609           0 :     ctx->slot++;
     610           0 :     ctx->hashcnt = 0UL;
     611           0 :   }
     612             : 
     613           0 :   if( FD_UNLIKELY( !is_leader && !(ctx->hashcnt%ctx->hashcnt_per_tick ) ) ) {
     614             :     /* We finished a tick while not leader... save the current hash so
     615             :        it can be played back into the bank when we become the leader. */
     616           0 :     ulong tick_idx = (ctx->slot*ctx->ticks_per_slot+ctx->hashcnt/ctx->hashcnt_per_tick)%MAX_SKIPPED_TICKS;
     617           0 :     fd_memcpy( ctx->skipped_tick_hashes[ tick_idx ], ctx->hash, 32UL );
     618             : 
     619           0 :     ulong initial_tick_idx = (ctx->last_slot*ctx->ticks_per_slot+ctx->last_hashcnt/ctx->hashcnt_per_tick)%MAX_SKIPPED_TICKS;
     620           0 :     if( FD_UNLIKELY( tick_idx==initial_tick_idx ) ) FD_LOG_ERR(( "Too many skipped ticks from slot %lu to slot %lu, chain must halt", ctx->last_slot, ctx->slot ));
     621           0 :   }
     622             : 
     623           0 :   if( FD_UNLIKELY( is_leader && !(ctx->hashcnt%ctx->hashcnt_per_tick) ) ) {
     624             :     /* We ticked while leader... tell the leader bank. */
     625           0 :     ctx->register_tick_func( ctx->arg, ctx->current_leader_slot, ctx->hash );
     626             :     /* And send an empty microblock (a tick) to the shred tile. */
     627           0 :     fd_poh_tile_publish_tick( ctx, ctx->hash, 0 );
     628           0 :   }
     629             : 
     630           0 :   if( FD_UNLIKELY( is_leader && ctx->slot>ctx->next_leader_slot ) ) {
     631             :     /* We ticked while leader and are no longer leader... transition
     632             :        the state machine. */
     633           0 :     FD_TEST( !max_remaining_microblocks );
     634           0 :     fd_poh_tile_no_longer_leader( ctx );
     635           0 :     ctx->expect_sequential_leader_slot = ctx->slot;
     636             : 
     637           0 :     double tick_per_ns = fd_tempo_tick_per_ns( NULL );
     638           0 :     fd_histf_sample( ctx->slot_done_delay, (ulong)((double)(fd_log_wallclock()-ctx->reset_slot_start_ns)/tick_per_ns) );
     639           0 :   }
     640             : 
     641           0 :   return 1;
     642           0 : }
     643             : 
     644             : void
     645           0 : fd_poh_tile_init_stakes( fd_poh_tile_ctx_t * ctx, uchar const * stakes_msg ) {
     646           0 :   fd_stake_ci_stake_msg_init( ctx->stake_ci, stakes_msg );
     647           0 : }
     648             : 
     649             : void
     650           0 : fd_poh_tile_fini_stakes( fd_poh_tile_ctx_t * ctx ) {
     651           0 :   fd_stake_ci_stake_msg_fini( ctx->stake_ci );
     652             : 
     653             :    /* It might seem like we do not need to do state transitions in and
     654             :        out of being the leader here, since leader schedule updates are
     655             :        always one epoch in advance (whether we are leader or not would
     656             :        never change for the currently executing slot) but this is not
     657             :        true for new ledgers when the validator first boots.  We will
     658             :        likely be the leader in slot 1, and get notified of the leader
     659             :        schedule for that slot while we are still in it.
     660             : 
     661             :        For safety we just handle both transitions, in and out, although
     662             :        the only one possible should be into leader. */
     663           0 :     ulong next_leader_slot_after_frag = fd_poh_tile_next_leader_slot( ctx );
     664             : 
     665           0 :     int currently_leader  = ctx->slot>=ctx->next_leader_slot;
     666           0 :     int leader_after_frag = ctx->slot>=next_leader_slot_after_frag;
     667             : 
     668           0 :     FD_LOG_INFO(( "stake_update(before_leader=%lu,after_leader=%lu)",
     669           0 :                   ctx->next_leader_slot,
     670           0 :                   next_leader_slot_after_frag ));
     671             : 
     672           0 :     ctx->next_leader_slot = next_leader_slot_after_frag;
     673           0 :     if( FD_UNLIKELY( currently_leader && !leader_after_frag ) ) {
     674             :       /* Shouldn't ever happen, otherwise we need to do a state
     675             :          transition out of being leader. */
     676           0 :       FD_LOG_ERR(( "stake update caused us to no longer be leader in an active slot" ));
     677           0 :     }
     678             : 
     679             :     /* Nothing to do if we transition into being leader, since it
     680             :        will just get picked up by the regular tick loop. */
     681           0 :     return;
     682           0 : }
     683             : 
     684             : void
     685           0 : fd_poh_tile_done_packing( fd_poh_tile_ctx_t * ctx, ulong microblocks_in_slot ) {
     686           0 :   FD_TEST( ctx->microblocks_lower_bound<=ctx->max_microblocks_per_slot );
     687           0 :   FD_LOG_INFO(( "done_packing(slot=%lu,seen_microblocks=%lu,microblocks_in_slot=%lu)",
     688           0 :                     ctx->slot,
     689           0 :                     ctx->microblocks_lower_bound,
     690           0 :                     microblocks_in_slot ));
     691           0 :   ctx->microblocks_lower_bound += ctx->max_microblocks_per_slot - microblocks_in_slot;
     692           0 : }
     693             : 
     694             : void
     695             : fd_poh_tile_publish_microblock( fd_poh_tile_ctx_t * ctx,
     696             :                                 ulong               sig,
     697             :                                 ulong               slot,
     698             :                                 ulong               hashcnt_delta,
     699             :                                 fd_txn_p_t *        txns,
     700           0 :                                 ulong               txn_cnt ) {
     701           0 :   uchar * dst = (uchar *)ctx->get_microblock_buffer_func( ctx->arg );
     702           0 :   FD_TEST( slot>=ctx->reset_slot );
     703           0 :   fd_entry_batch_meta_t * meta = (fd_entry_batch_meta_t *)dst;
     704           0 :   meta->parent_offset = 1UL+slot-ctx->reset_slot;
     705           0 :   meta->reference_tick = (ctx->hashcnt/ctx->hashcnt_per_tick) % ctx->ticks_per_slot;
     706           0 :   meta->block_complete = !ctx->hashcnt;
     707             : 
     708           0 :   dst += sizeof(fd_entry_batch_meta_t);
     709           0 :   fd_entry_batch_header_t * header = (fd_entry_batch_header_t *)dst;
     710           0 :   header->hashcnt_delta = hashcnt_delta;
     711           0 :   fd_memcpy( header->hash, ctx->hash, 32UL );
     712             : 
     713           0 :   dst += sizeof(fd_entry_batch_header_t);
     714           0 :   ulong payload_sz = 0UL;
     715           0 :   ulong included_txn_cnt = 0UL;
     716           0 :   for( ulong i=0UL; i<txn_cnt; i++ ) {
     717           0 :     fd_txn_p_t * txn = &txns[ i ];
     718           0 :     if( FD_UNLIKELY( !(txn->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS) ) ) continue;
     719             : 
     720           0 :     fd_memcpy( dst, txn->payload, txn->payload_sz );
     721           0 :     payload_sz += txn->payload_sz;
     722           0 :     dst        += txn->payload_sz;
     723           0 :     included_txn_cnt++;
     724           0 :   }
     725           0 :   header->txn_cnt = included_txn_cnt;
     726             : 
     727             :   /* We always have credits to publish here, because we have a burst
     728             :      value of 3 credits, and at most we will publish_tick() once and
     729             :      then publish_became_leader() once, leaving one credit here to
     730             :      publish the microblock. */
     731           0 :   ulong tspub = (ulong)fd_frag_meta_ts_comp( fd_tickcount() );
     732           0 :   ulong sz = sizeof(fd_entry_batch_meta_t)+sizeof(fd_entry_batch_header_t)+payload_sz;
     733           0 :   ctx->publish_microblock_func( ctx->arg, tspub, sig, sz );
     734           0 : }
     735             : 
     736             : void
     737             : fd_poh_tile_process_packed_microblock( fd_poh_tile_ctx_t * ctx,
     738             :                                        ulong               target_slot,
     739             :                                        ulong               sig,
     740             :                                        fd_txn_p_t *        txns,
     741             :                                        ulong               txn_cnt,
     742           0 :                                        uchar               mixin_hash[ static 32 ] ) {
     743           0 :   if( FD_UNLIKELY( !ctx->microblocks_lower_bound ) ) {
     744           0 :     double tick_per_ns = fd_tempo_tick_per_ns( NULL );
     745           0 :     fd_histf_sample( ctx->first_microblock_delay, (ulong)((double)(fd_log_wallclock()-ctx->reset_slot_start_ns)/tick_per_ns) );
     746           0 :   }
     747             : 
     748           0 :   if( FD_UNLIKELY( target_slot!=ctx->next_leader_slot || target_slot!=ctx->slot ) ) {
     749           0 :     FD_LOG_ERR(( "packed too early or late target_slot=%lu, current_slot=%lu. highwater_leader_slot=%lu",
     750           0 :                  target_slot, ctx->slot, ctx->highwater_leader_slot ));
     751           0 :   }
     752             : 
     753           0 :   FD_TEST( ctx->current_leader_slot!=FD_SLOT_NULL );
     754           0 :   FD_TEST( ctx->microblocks_lower_bound<ctx->max_microblocks_per_slot );
     755           0 :   ctx->microblocks_lower_bound += 1UL;
     756             : 
     757           0 :   ulong executed_txn_cnt = 0UL;
     758           0 :   for( ulong i=0; i<txn_cnt; i++ ) { executed_txn_cnt += !!(txns[ i ].flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS); }
     759             : 
     760             :   /* We don't publish transactions that fail to execute.  If all the
     761             :      transctions failed to execute, the microblock would be empty, causing
     762             :      solana labs to think it's a tick and complain.  Instead we just skip
     763             :      the microblock and don't hash or update the hashcnt. */
     764           0 :   if( FD_UNLIKELY( !executed_txn_cnt ) ) return;
     765             : 
     766           0 :   FD_LOG_DEBUG(( "rx packed mblk - target_slot: %lu, txn_cnt: %lu", target_slot, executed_txn_cnt ));
     767             : 
     768           0 :   uchar data[ 64 ];
     769           0 :   fd_memcpy( data, ctx->hash, 32UL );
     770           0 :   fd_memcpy( data+32UL, mixin_hash, 32UL );
     771           0 :   fd_sha256_hash( data, 64UL, ctx->hash );
     772             : 
     773           0 :   ctx->hashcnt++;
     774           0 :   FD_TEST( ctx->hashcnt>ctx->last_hashcnt );
     775           0 :   ulong hashcnt_delta = ctx->hashcnt - ctx->last_hashcnt;
     776             : 
     777             :   /* The hashing loop above will never leave us exactly one away from
     778             :      crossing a tick boundary, so this increment will never cause the
     779             :      current tick (or the slot) to change, except in low power mode
     780             :      for development, in which case we do need to register the tick
     781             :      with the leader bank.  We don't need to publish the tick since
     782             :      sending the microblock below is the publishing action. */
     783           0 :   if( FD_UNLIKELY( !(ctx->hashcnt%ctx->hashcnt_per_slot ) ) ) {
     784           0 :     ctx->slot++;
     785           0 :     FD_LOG_WARNING(("SLOT INC: %lu", ctx->slot));
     786           0 :     ctx->hashcnt = 0UL;
     787           0 :   }
     788             : 
     789           0 :   ctx->last_slot    = ctx->slot;
     790           0 :   ctx->last_hashcnt = ctx->hashcnt;
     791             : 
     792           0 :   if( FD_UNLIKELY( !(ctx->hashcnt%ctx->hashcnt_per_tick ) ) ) {
     793           0 :     ctx->register_tick_func( ctx->arg, ctx->current_leader_slot, ctx->hash );
     794           0 :     if( FD_UNLIKELY( ctx->slot>ctx->next_leader_slot ) ) {
     795             :       /* We ticked while leader and are no longer leader... transition
     796             :          the state machine. */
     797           0 :       fd_poh_tile_no_longer_leader( ctx );
     798           0 :     }
     799           0 :   }
     800             : 
     801           0 :   fd_poh_tile_publish_microblock( ctx, sig, target_slot, hashcnt_delta, txns, txn_cnt );
     802           0 : }
     803             : 
     804             : void
     805           0 : fd_poh_tile_during_housekeeping( fd_poh_tile_ctx_t * ctx ) {
     806           0 :   FD_MHIST_COPY( POH_TILE, BEGIN_LEADER_DELAY_SECONDS,     ctx->begin_leader_delay );
     807           0 :   FD_MHIST_COPY( POH_TILE, FIRST_MICROBLOCK_DELAY_SECONDS, ctx->first_microblock_delay );
     808           0 :   FD_MHIST_COPY( POH_TILE, SLOT_DONE_DELAY_SECONDS,        ctx->slot_done_delay );
     809           0 : }
     810             : 
     811             : fd_poh_tile_ctx_t *
     812             : fd_poh_tile_new( void * scratch,
     813             :                  void * arg,
     814             :                  fd_poh_tile_get_micoblock_buffer_func_t get_microblock_buffer_func,
     815             :                  fd_poh_tile_publish_microblock_func_t   publish_microblock_func,
     816             :                  fd_poh_tile_get_pack_buffer_func_t      get_pack_buffer_func,
     817             :                  fd_poh_tile_publish_pack_func_t         publish_pack_func,
     818             :                  fd_poh_tile_register_tick_func_t        register_tick_func,
     819           0 :                  fd_poh_tile_signal_leader_change_func_t signal_leader_change_func ) {
     820           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     821           0 :   fd_poh_tile_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_poh_tile_ctx_t ), sizeof( fd_poh_tile_ctx_t ) );
     822           0 :   void * stake_ci = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_ci_align(),              fd_stake_ci_footprint()            );
     823           0 :   void * sha256   = FD_SCRATCH_ALLOC_APPEND( l, FD_SHA256_ALIGN,                  FD_SHA256_FOOTPRINT                );
     824             : 
     825           0 : #define NONNULL( x ) (__extension__({                                        \
     826           0 :       __typeof__((x)) __x = (x);                                             \
     827           0 :       if( FD_UNLIKELY( !__x ) ) FD_LOG_ERR(( #x " was unexpectedly NULL" )); \
     828           0 :       __x; }))
     829             : 
     830           0 :   ctx->stake_ci = NONNULL( fd_stake_ci_join( fd_stake_ci_new( stake_ci, &ctx->identity_key ) ) );
     831           0 :   ctx->sha256 = NONNULL( fd_sha256_join( fd_sha256_new( sha256 ) ) );
     832           0 :   ctx->current_leader_slot = FD_SLOT_NULL;
     833             : 
     834           0 :   ctx->slot                  = 0UL;
     835           0 :   ctx->hashcnt               = 0UL;
     836           0 :   ctx->last_hashcnt          = 0UL;
     837           0 :   ctx->highwater_leader_slot = ULONG_MAX;
     838           0 :   ctx->next_leader_slot      = ULONG_MAX;
     839           0 :   ctx->reset_slot            = ULONG_MAX;
     840             : 
     841           0 :   ctx->expect_sequential_leader_slot = ULONG_MAX;
     842             : 
     843           0 :   ctx->microblocks_lower_bound = 0UL;
     844             : 
     845           0 :   fd_histf_join( fd_histf_new( ctx->begin_leader_delay, FD_MHIST_SECONDS_MIN( POH_TILE, BEGIN_LEADER_DELAY_SECONDS ),
     846           0 :                                                         FD_MHIST_SECONDS_MAX( POH_TILE, BEGIN_LEADER_DELAY_SECONDS ) ) );
     847           0 :   fd_histf_join( fd_histf_new( ctx->first_microblock_delay, FD_MHIST_SECONDS_MIN( POH_TILE, FIRST_MICROBLOCK_DELAY_SECONDS  ),
     848           0 :                                                             FD_MHIST_SECONDS_MAX( POH_TILE, FIRST_MICROBLOCK_DELAY_SECONDS  ) ) );
     849           0 :   fd_histf_join( fd_histf_new( ctx->slot_done_delay, FD_MHIST_SECONDS_MIN( POH_TILE, SLOT_DONE_DELAY_SECONDS  ),
     850           0 :                                                      FD_MHIST_SECONDS_MAX( POH_TILE, SLOT_DONE_DELAY_SECONDS  ) ) );
     851             : 
     852           0 :   ctx->arg = arg;
     853           0 :   ctx->get_microblock_buffer_func = get_microblock_buffer_func;
     854           0 :   ctx->publish_microblock_func = publish_microblock_func;
     855           0 :   ctx->get_pack_buffer_func = get_pack_buffer_func;
     856           0 :   ctx->publish_pack_func = publish_pack_func;
     857           0 :   ctx->register_tick_func = register_tick_func;
     858           0 :   ctx->signal_leader_change_func = signal_leader_change_func;
     859             : 
     860           0 :   ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, 1UL );
     861           0 :   if( FD_UNLIKELY( scratch_top > (ulong)scratch + fd_poh_tile_footprint() ) )
     862           0 :     FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - fd_poh_tile_footprint(), scratch_top, (ulong)scratch + fd_poh_tile_footprint() ));
     863             : 
     864           0 :   return ctx;
     865           0 : }

Generated by: LCOV version 1.14