5.4 Programs
5.4.1 Validity
5.4.2 Modules
5.4.3 Steps
5.4.4 Local Steps
5.4.5 Consensus Steps
5.4.6 Computations
On this page:
5.4.5.1 Statements
5.4.5.1.1 commit
commit
5.4.5.1.2 only and each
5.4.5.1.3 Participant.set and .set
5.4.5.1.4 while
var
invariant
while
5.4.5.1.5 continue
continue
5.4.5.1.6 parallel_  reduce
parallel_  reduce
5.4.5.2 Expressions
5.4.5.2.1 this
5.4.5.2.2 transfer
transfer
5.4.5.2.3 require
require
5.4.5.2.4 check  Commitment
check  Commitment
5.4.5 Consensus Steps

A Reach consensus step occurs in the continuation of a consensus transfer statement. It represents the actions taken by the consensus network contract of an application.

5.4.5.1 Statements

Any statements valid for a computation are valid for a consensus step. However, some additional statements are allowed.

5.4.5.1.1 commit

commit(); 

A commit statement, written commit();, commits to statement’s continuation as the next step of the DApp computation. In other words, it ends the current consensus step and allows more local steps.

5.4.5.1.2 only and each

only and each are allowed in consensus steps and are executed by backends once they observe the completion of the consensus step (i.e., after the associated commit statement.)

5.4.5.1.3 Participant.set and .set

Participant.set(PART, ADDR);
PART.set(ADDR); 

After execution, the given participant is fixed to the given address. It is invalid to attempt to .set a participant class. If a backend is running for this participant and its address does not match the given address, then it will abort. This may only occur within a consensus step.

Workshop: Relay Account is a good introductory project that demonstrates how to use this feature of Reach.

5.4.5.1.4 while

var [ heap1, heap2 ] = [ 21, 21 ];
invariant(balance() == 2 * wagerAmount);
while ( heap1 + heap2 > 0 ) {
  ....
  [ heap1, heap2 ] = [ heap1 - 1, heap2 ];
  continue; } 

A while statement may occur within a consensus step and is written:

var LHS = INIT_EXPR;
invariant(INVARIANT_EXPR);
while( COND_EXPR ) BLOCK 

where LHS is a valid left-hand side of an identifier definition where the expression INIT_EXPR is the right-hand side, and INVARIANT_EXPR is an expression, called the loop invariant, that must be true before and after every execution of the block BLOCK, and if COND_EXPR is true, then the block executes, and if not, then the loop terminates and control transfers to the continuation of the while statement. The identifiers bound by LHS are bound within INVARIANT_EXPR, COND_EXPR, BLOCK, and the tail of the while statement.

Read about finding loop invariants in the Reach guide.

5.4.5.1.5 continue

[ heap1, heap2 ] = [ heap1 - 1, heap2 ];
continue; 

A continue statement may occur within a while statement’s block and is written:

LHS = UPDATE_EXPR;
continue; 

where the identifiers bound by LHS are a subset of the variables bound by the nearest enclosing while statement and UPDATE_EXPR is an expression which may be bound by LHS.

A continue statement is a terminator statement, so it must have an empty tail.

A continue statement may be written without the preceding identifier update, which is equivalent to writing

[] = [];
continue; 

A continue statement must be dominated by a consensus transfer, which means that the body of a while statement must always commit(); before calling continue;. This restriction may be lifted in future versions of Reach, which will perform termination checking.

5.4.5.1.6 parallel_reduce

const [ keepGoing, as, bs ] =
  parallel_reduce([ true, 0, 0 ])
  .invariant(balance() == 2 * wager)
  .while(keepGoing)
  .case(Alice, (() => ({
    when: declassify(interact.keepGoing()) })),
    () => {
      each([Alice, Bob], () => {
        interact.roundWinnerWas(true); });
      return [ true, 1 + as, bs ]; })
  .case(Bob, (() => ({
    when: declassify(interact.keepGoing()) })),
    () => {
      each([Alice, Bob], () => {
        interact.roundWinnerWas(false); });
      return [ true, as, 1 + bs ]; })
  .timeout(deadline, () => {
    showOutcome(TIMEOUT)();
    race(Alice, Bob).publish();
    return [ false, as, bs ]; });

A parallel reduce statement is written:

const LHS =
  parallel_reduce(INIT_EXPR)
  .invariant(INVARIANT_EXPR)
  .while(COND_EXPR)
  .case(PART_EXPR,
    PUBLISH_EXPR,
    PAY_EXPR,
    CONSENSUS_EXPR)
  .timeout(DELAY_EXPR, () =>
    TIMEOUT_BLOCK);

The LHS and INIT_EXPR are like the initialization component of a while loop; and, the .invariant and .while components are like the invariant and condition of a while loop; while the .case and .timeout components are like the corresponding components of a fork statement.

The .case component may be repeated many times, provided the PART_EXPRs each evaluate to a unique participant, just like in a fork statement.

A parallel reduce statement is essentially an abbreviation of pattern of a while loop combined with a fork statement that you could write yourself. This is an extremely common pattern in decentralized applications.

The idea is that there are some values (the LHS) which after intialization will be repeatedly updated uniquely by each of the racing participants until the condition does not hold.

var LHS = INIT_EXPR;
invariant(INVARIANT_EXPR)
while(COND_EXPR) {
  fork()
  .case(PART_EXPR,
    PUBLISH_EXPR,
    PAY_EXPR,
    () => {
      LHS = CONSENSUS_EXPR;
      continue; })
  .timeout(DELAY_EXPR, () =>
    TIMEOUT_BLOCK);
}

5.4.5.2 Expressions

Any expressions valid for a computation are valid for a consensus step. However, some additional expressions are allowed.

5.4.5.2.1 this

Inside of a consensus step, this refers to the address of the participant that performed the consensus transfer. This is useful when the consensus transfer was initiated by a race expression.

5.4.5.2.2 transfer

transfer(10).to(Alice) 

A transfer expression, written transfer(AMOUNT_EXPR).to(PART), where AMOUNT_EXPR is an expression that evaluates to a natural number and PART is a participant identifier, performs a transfer of network tokens from the contract to the named participant. AMOUNT_EXPR must evaluate to less than or equal to the balance of network tokens in the contract account. A transfer expression may only occur within a consensus step.

5.4.5.2.3 require

require( claim, [msg] ) 

A requirement where claim evaluates to true with honest participants. This may only appear in a consensus step. It accepts an optional bytes argument, which is included in any reported violation.

5.4.5.2.4 checkCommitment

checkCommitment( commitment, salt, x ) 

Makes a requirement that commitment is the digest of salt and x. This is used in a consensus step after makeCommitment was used in a local step.