Engineering Rapid Time Control in Reversteem: On-Chain Clock Mechanics and Deterministic Timeout Resolution

in Steem Dev • 2 hours ago

5% of the rewards of this post are for @steem.amal

1000132452.jpg

Building turn-based board games directly on top of a social blockchain’s post-and-comment layer introduces unique synchronization challenges. Without a centralized server or custom smart contracts, game state must be deterministically reconstructed on the client side by replaying chronologically ordered on-chain event logs.

When implementing Rapid Time Control (a 5-minute per-move limit) in Reversteem, two specific technical problems arise:

  1. Managing move clocks without relying on real-time backend state or continuous block triggers.
  1. Resolving timing race conditions—such as a late move broadcast simultaneously with a valid timeout claim.

Here is an architectural overview of how Reversteem specifies, validates, and enforces rapid time control within a 100% serverless protocol.


1. Metadata Specification & Time Control Presets

Time control configurations are specified by the game author inside the json_metadata field of the game’s root post during creation (type: "game_start").

{
  "app": "reversteem/0.1",
  "type": "game_start",
  "black": "alice",
  "white": null,
  "timeoutMinutes": 5,
  "tags": ["rapid", "elo-1200", "reversi", "othello", "steem"]
}

When creating a Rapid game, timeoutMinutes is explicitly set to 5. The protocol automatically prefixes the post’s metadata tags with rapid for discovery and filtering across the ecosystem.


2. On-Chain Clock Mechanics & Deterministic Replay

Steem block timestamps are recorded in UTC without explicit offset designators (e.g., "2026-09-29T12:00:00"). To ensure deterministic replay across diverse client runtimes without timezone drift, the client appends 'Z' to force standard UTC parsing prior to chronologically sorting all replies:

function steemDate(ts) {
  if (typeof ts === 'string' && !ts.endsWith('Z')) ts += 'Z';
  return new Date(ts);
}

replies.sort((a, b) => steemDate(a.created) - steemDate(b.created));

Clock State Rules:

  • Clock Initialization: The timer does not start when the root post is submitted. Instead, lastMoveTime is set to the created timestamp of the accepted Join Comment. This ensures the Black player isn't penalized before White enters the game.
  • Exemption Rule: The first move (appliedMoves === 0) is exempt from timeout enforcement.
  • Clock Reset: Each accepted Move Comment updates lastMoveTime to that comment's immutable on-chain timestamp.

3. Claiming Timeout & Race Condition Safety

Because Steem lacks an automated event-scheduler smart contract, timeouts cannot trigger passively; the non-active player must post a direct timeout_claim reply:

{
  "app": "reversteem/0.1",
  "action": "timeout claim",
  "claimAgainst": "black",
  "moveNumber": 12
}

During replay execution, timeout_claim actions undergo a strict validation pipeline:

  1. State Check: finished must be false.
  1. Move Sequence Alignment: claim.moveNumber must exactly equal the engine's current appliedMoves count.
  1. Elapsed Time Threshold: The delta between the claim timestamp and lastMoveTime must be greater than or equal to timeoutMinutes:

minutesPassed = (steemDate(claim.created) - steemDate(lastMoveTime)) / 60000 >= timeoutMinutes

  1. Turn & Author Verification: claim.claimAgainst must match the current turn color, and the claim author must match the opposing player.

Race Condition Mitigation

If the non-active player submits a timeout_claim while the active player broadcasts a delayed move, standard timestamp ordering resolves the conflict deterministically. Once a valid timeout_claim timestamp passes the threshold condition during replay processing, the game transitions immediately to finished = true. Any subsequent move comments recorded in the blockchain's history after the timeout boundary are skipped during replay validation.


4. UI Architecture: Local State vs. On-Chain Sync

To maintain responsiveness without overloading RPC infrastructure, the client interface isolates local countdown rendering from block propagation:

  • Local Clock Timer: A local setInterval updates the visual board timer based on Date.now() - lastMoveTime.
  • Polling Loop: GameView polls the Steem network every 15 seconds using non-overlapping setTimeout instances to check for new reply operations.
  • State Immutability: State updates merge incrementally rather than replacing local reactive objects, preventing component flicker on high-frequency UI updates.

5. GitHub Repository: https://github.com/puncakbukit/reversteem


Assisted by https://gemini.google.com/.

See also:

Sort:  

Upvoted! Thank you for supporting witness @jswit.