$$ \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 \HandleProposal {\mathrm{HandleProposal}} \newcommand \VerifyProposal {\mathrm{VerifyProposal}} \newcommand \IsCommittable {\mathrm{IsCommittable}} \newcommand \Relay {\mathrm{Relay}} \newcommand \Broadcast {\mathrm{Broadcast}} \newcommand \Vote {\mathrm{Vote}} \newcommand \Sortition {\mathrm{Sortition}} \newcommand \Proposal {\mathrm{Proposal}} \newcommand \Bundle {\mathrm{Bundle}} \newcommand \Hash {\mathrm{Hash}} \newcommand \Encode {\mathrm{Encode}} \newcommand \bh {\mathrm{bh}} \newcommand \Soft {\mathit{soft}} \newcommand \Cert {\mathit{cert}} \newcommand \Next {\mathit{next}} \newcommand \pr {\mathit{proposal}} \newcommand \c {\mathit{credentials}} $$
Proposal Handler
The proposal handler is triggered when a node receives a full proposal message.
A proposal-value and a full proposal are related but separate constructions. This is motivated by a slower gossiping time of a full proposal, compared to a much more succinct and therefore quickly gossiped proposal-value.
A proposal-value contains four fields:
-
The original period in which this block was proposed.
-
The original proposer’s address.
-
The block digest, equal to the block header’s hash (including a domain separator). This field expands to \( \Hash(\texttt{“BH”} || \Encode(\bh)) \), where \( \texttt{“BH”} \) is the domain separator for a “block header”, and the encoding function is the msgpack of the block header (\( \bh \)).
-
A hash of the proposal, \( \Hash(\Proposal) \). This field expands to \( \Hash(\texttt{“PL”} || \Encode(\Proposal)) \), where \( \Proposal \) represents the unauthenticated proposal, \( \texttt{“PL”} \) is the domain separator for a “payload”, and the encoding function is the msgpack of the \( \Proposal \).
⚙️ IMPLEMENTATION
Proposal-value structure.
Domain separators for block header and payload.
On the other hand, an unauthenticated proposal contains a full block and all extra data for the block validation:
-
The original period in which this block was proposed.
-
The original proposer’s address.
-
A full block (header and payset).
-
A seed proof \( \pi_{seed} \).
Note that the original period and proposer’s address are the same as the associated proposal-value. The seed proof \( \pi_{seed} \) is used to verify the seed computation.
⚙️ IMPLEMENTATION
Unauthenticated proposal structure.
⚙️ IMPLEMENTATION
In the reference implementation, the unauthenticated proposal is sent to the network linked to a previously emitted proposal vote. These are sent together in a struct defined as a compound message, which avoids the edge case of receiving proposal and proposal-value messages in an unfavorable order. Consider the following edge case: a node observes a proposal before receiving its supporting proposal-value. It discards the proposal (to avoid DDoS attacks). Right after the node receives the related proposal-value, it goes through all the steps, and certifies this block, but needs to request the previously discarded proposal to be able to commit it and advance a round. If this happens to enough nodes (voting stake), the network might move to a second period. In the new period, the proposal is broadcast and committed fast, since the proposal step is skipped (having carried over the staged and frozen values).
Algorithm
In the following pseudocode the frozen value \( \mu \) is either:
- The highest priority observed proposal-value in the current \((r, p)\) context (i.e., the lowest hashed according to the priority function), or
- \( \bot \) if the node has observed no valid proposal vote.
The staged value \( \sigma \) is either:
- The sole proposal-value for which a \( \Bundle_\Soft \) has been observed in the current \((r, p)\) context (see normative section), or
- \( \bot \) if the node has observed no valid \( \Bundle_\Soft \).
The pinned value \( \bar{v} \) is a proposal-value that was a staged value in a previous period. When available, this value is used to fast-forward the first steps of the protocol when a \( \Next \) vote has been successful.
\( \textbf{Algorithm 6} \text{: Handle Proposal} \)
$$ \begin{aligned} &\text{1: } \PSfunction \HandleProposal(\pr) \\ &\text{2: } \quad v \gets \Proposal_v(\pr, \pr_p, \pr_I) \\ &\text{3: } \quad \PSif \exists \Bundle(r+1, 0, \Soft, v) \in B \PSthen \\ &\text{4: } \quad \quad \Relay(\pr) \\ &\text{5: } \quad \quad \PSreturn \PScomment{Future round, do not observe (node is behind)} \\ &\text{6: } \quad \PSendif \\ &\text{7: } \quad \PSif \PSnot \VerifyProposal(\pr) \lor \pr \in P \PSthen \\ &\text{8: } \quad \quad \PSreturn \PScomment{Ignore proposal} \\ &\text{9: } \quad \PSendif \\ &\text{10:} \quad \PSif v \notin \{\sigma, \bar{v}, \mu\} \PSthen \\ &\text{11:} \quad \quad \PSreturn \PScomment{Ignore proposal} \\ &\text{12:} \quad \PSendif \\ &\text{13:} \quad \Relay(\pr) \\ &\text{14:} \quad P \gets P \cup \pr \\ &\text{15:} \quad \PSif \IsCommittable(v) \land s \le \Cert \PSthen \\ &\text{16:} \quad \quad \PSfor a \in A \PSdo \\ &\text{17:} \quad \quad \quad \c \gets \Sortition(a_I, r, p, \Cert) \\ &\text{18:} \quad \quad \quad \PSif \c_j > 0 \PSthen \\ &\text{19:} \quad \quad \quad \quad \Broadcast(\Vote(a_I, r, p, \Cert, v, \c)) \\ &\text{20:} \quad \quad \quad \PSendif \\ &\text{21:} \quad \quad \PSendfor \\ &\text{22:} \quad \PSendif \\ &\text{23: } \PSendfunction \end{aligned} $$
⚙️ IMPLEMENTATION
Proposal handler reference implementation.
The node starts by performing a series of checks, after which it will either:
-
Ignore the received proposal, discarding it and emitting no output, or
-
Relay, observe, and produce an output according to the current context and the characteristics of the proposal.
The node checks if the proposal is from the first period of the next round (Line 3), in which case, the node relays this proposal and then ignores it for the operations of the current round.
Whenever the node catches up (i.e., observes a round change), and only if necessary, it will request this proposal back from the network.
The node checks (Line 7) if the proposal is invalid or has already been observed. Any one of those conditions is enough to discard and ignore the incoming proposal.
Finally, the node checks (Line 10) if the associated proposal value is either a special proposal-value for the current round and period (\( \sigma \), \( \mu \)) or the pinned proposal-value (\( \bar{v} \)). Any full proposal whose proposal-value does not match one of these is ignored.
For formal details on special values, refer to the normative section.
Once the checks have been passed, the node relays and observes the proposal (Lines 13 and 14), by adding it to the observed proposals set \( P \).
Next, only if the proposal-value is committable (meaning the staged value is set for a proposal, and said proposal has already been observed and is available) and the current step is lower than or equal to a \( \Cert \) step (i.e., is not yet in a recovery step), the node plays for each online account (registered on the node), performing a \( \Sortition \)to select the certification committee members.
For each selected account, a \( \Vote_\Cert \) for the current proposal-value is broadcast.