Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

$$ \newcommand \PSfunction {\textbf{function }} \newcommand \PSreturn {\textbf{return }} \newcommand \PSendfunction {\textbf{end function}} \newcommand \PSswitch {\textbf{switch }} \newcommand \PScase {\textbf{case }} \newcommand \PSdefault {\textbf{default}} \newcommand \PSendswitch {\textbf{end switch}} \newcommand \PSfor {\textbf{for }} \newcommand \PSendfor {\textbf{end for}} \newcommand \PSwhile {\textbf{while }} \newcommand \PSendwhile {\textbf{end while}} \newcommand \PSdo {\textbf{ do}} \newcommand \PSif {\textbf{if }} \newcommand \PSelseif {\textbf{else if }} \newcommand \PSelse {\textbf{else}} \newcommand \PSthen {\textbf{ then}} \newcommand \PSendif {\textbf{end if}} \newcommand \PSnot {\textbf{not }} \newcommand \PScomment {\qquad \small \textsf} $$

$$ \newcommand \TP {\mathrm{TxPool}} \newcommand \AD {\mathrm{assemblyDeadline}} \newcommand \AW {\mathrm{assemblyWait}} \newcommand \AssembleBlock {\mathrm{AssembleBlock}} \newcommand \BlockEval {\mathrm{BlockEvaluator}} \newcommand \EB {\mathrm{emptyBlock}} \newcommand \r {\mathrm{round}} \newcommand \nil {\mathit{nil}} $$

Block Assembly

The \( \TP \) is responsible for populating the payset of a block, a process referred to as BlockAssembly.

The BlockAssembly is a time-bound algorithm that manages the flow of transactions into the pending \( \BlockEval \) and stops ingestion once timing constraints are reached.

It also handles possible desynchronizations between the \( \TP.\r \) (the current round as perceived by the \( \TP \)) and the actual round being assembled by the pending \( \BlockEval \). This discrepancy arises based on how often the Update function has been invoked.

The following pseudocode outlines a high-level view of how BlockAssembly operates:


\( \textbf{Algorithm 5} \text{: Block Assembly} \)

$$ \begin{aligned} &\text{1: } \PSfunction \AssembleBlock(r) \\ &\text{2: } \quad \PSif \TP.\r < r - 2 \PSthen \\ &\text{3: } \quad \quad \PSreturn \AssembleBlock.\EB(r) \\ &\text{4: } \quad \PSendif \\ &\text{5: } \quad \PSif r < \TP.\r \PSthen \\ &\text{6: } \quad \quad \PSreturn \nil \\ &\text{7: } \quad \PSendif \\ &\text{8: } \quad \AD \gets \r.\mathrm{startTime}() + \delta_{\AD} \\ &\text{9: } \quad \text{Wait until } \AD \lor (\TP.\r = r \land \BlockEval \text{ is done}) \\ &\text{10:} \quad \PSif \lnot \BlockEval.\mathrm{done}() \PSthen \\ &\text{11:} \quad \quad \PSif \TP.\r > r \PSthen \\ &\text{12:} \quad \quad \quad \PSreturn \nil \PScomment{r is behind } \TP.\r \\ &\text{13:} \quad \quad \PSendif \\ &\text{14:} \quad \quad \AD \gets \AD + \epsilon_{\AW} \\ &\text{15:} \quad \quad \text{Wait until } \AD \lor (\TP.\r = r \land \BlockEval \text{ is done}) \\ &\text{16:} \quad \quad \PSif \lnot \BlockEval.\mathrm{done}() \PSthen \\ &\text{17:} \quad \quad \quad \PSreturn \AssembleBlock.\EB(r) \PScomment{Ran out of time} \\ &\text{18:} \quad \quad \PSendif \\ &\text{19:} \quad \quad \PSif \TP.\r > r \PSthen \\ &\text{20:} \quad \quad \quad \PSreturn \nil \PScomment{Requested round is behind transaction pool round} \\ &\text{21:} \quad \quad \PSelseif \TP.\r = r - 1 \PSthen \\ &\text{22:} \quad \quad \quad \PSreturn \AssembleBlock.\EB(r) \\ &\text{23:} \quad \quad \PSelseif \TP.\r < r \PSthen \\ &\text{24:} \quad \quad \quad \PSreturn \nil \\ &\text{25:} \quad \quad \PSendif \\ &\text{26:} \quad \PSendif \\ &\text{27:} \quad \PSreturn \BlockEval.\mathrm{block} \\ &\text{28: } \PSendfunction \end{aligned} $$


⚙️ IMPLEMENTATION

Block assembly reference implementation.

This algorithm begins by taking a target round \( r \), for which a new block is to be assembled.

It first checks the round currently perceived by the \( \TP \), which matches the round being handled by the pending \( \BlockEval \).

  • If the \( \TP.\r \) is significantly behind \( r \): an empty block is immediately assembled and returned, as there’s no time to catch up.

  • If the \( \TP \) is already ahead of \( r \): no action is needed, as \( \TP \) is simply ahead of the network’s current state.

Next, the algorithm waits for the assembly deadline \( \delta_{\AD} \). During this time, the pending \( \BlockEval \) is expected to notify the completed block assembly in the background via the Ingestion function, and that it is caught up to the round \( r \).

If this doesn’t happen by the deadline, the algorithm performs another round of checks:

  • If the \( \TP.\r \) is now ahead of \( r \): the process is aborted, waiting for the network to catch up. This should rarely happen.

  • Othwewise, if the \( \TP \) is still behind: an additional wait period \( \epsilon_{\AW} \) is introduced.

After this extra wait, similar checks are repeated:

  • If the \( \TP \) is still too far behind: there is no more time to wait, and the algorithm exits.

  • Otherwise: the algorithm proceeds.

If all checks pass and timing constraints are met without returning early (an empty block or a \( \nil \) value), the pending \( \BlockEval \) finally provides the fully assembled block for round \( r \).