diff --git a/specs/SUMMARY.md b/specs/SUMMARY.md index 7b6f650b0..51db9d909 100644 --- a/specs/SUMMARY.md +++ b/specs/SUMMARY.md @@ -30,6 +30,7 @@ - [Dispute Game Interface](./experimental/fault-proof/dispute-game-interface.md) - [Fault Dispute Game](./experimental/fault-proof/fault-dispute-game.md) - [Honest Challenger](./experimental/fault-proof/honest-challenger-fdg.md) + - [Bond Incentives](./experimental/fault-proof/bond-incentives.md) - [Glossary](./glossary.md) - [Meta](./meta/index.md) - [Linting](./meta/linting.md) diff --git a/specs/experimental/fault-proof/bond-incentives.md b/specs/experimental/fault-proof/bond-incentives.md new file mode 100644 index 000000000..6a4a3c9f4 --- /dev/null +++ b/specs/experimental/fault-proof/bond-incentives.md @@ -0,0 +1,47 @@ +# Bond Incentives + +Bonds is an add-on to the core [Fault Dispute Game](./fault-dispute-game.md). The core game mechanics are +designed to ensure honesty as the best response to winning subgames. By introducing financial incentives, +Bonds makes it worthwhile for honest challengers to participate. +Without the bond reward incentive, the FDG will be too costly for honest players to participate in given the +cost of verifying and making claims. + +Implementations may allow the FDG to directly receive bonds, or delegate this responsibility to another entity. +Regardless, there must be a way for the FDG to query and distribute bonds linked to a claim. + +Bonds are integrated into the FDG in two areas: + +- Moves +- Subgame Resolution + +## Moves + +Moves must be adequately bonded to be added to the FDG. This document does not specify a +scheme for determining the minimum bond requirement. FDG implementations should define a function +computing the minimum bond requirement with the following signature: + +```solidity +function getRequiredBond(Position _movePosition) public pure returns (uint256 requiredBond_) +``` + +As such, attacking or defending requires a check for the `getRequiredBond()` amount against the bond +attached to the move. To incentivize participation, the minimum bond should cover the cost of a possible +counter to the move being added. Thus, the minimum bond depends only on the position of the move that's added. + +## Subgame Resolution + +If a subgame root resolves incorrectly, then its bond is distributed to the **leftmost claimant** that countered +it. This creates an incentive to identify the earliest point of disagreement in an execution trace. +The subgame root claimant gets back its bond iff it resolves correctly. + +At maximum game depths, where a claimant counters a bonded claim via `step`, the bond is instead distributed +to the account that successfully called `step`. + +### Leftmost Claim Incentives + +There exists defensive positions that cannot be countered, even if they hold invalid claims. These positions +are located on the same level as honest claims, but situated to its right (i.e. its gindex > honest claim's). + +An honest challenger can always successfully dispute any sibling claims not positioned to the right of an honest claim. +The leftmost payoff rule encourages such disputes, ensuring only one claim is leftmost at correct depths. +This claim will be the honest one, and thus bond rewards will be directed exclusively to honest claims. diff --git a/specs/experimental/fault-proof/fault-dispute-game.md b/specs/experimental/fault-proof/fault-dispute-game.md index 6e32a568d..3867997da 100644 --- a/specs/experimental/fault-proof/fault-dispute-game.md +++ b/specs/experimental/fault-proof/fault-dispute-game.md @@ -143,7 +143,11 @@ Note that there can be multiple positions covering the same _trace index_. This is an immutable, preset to a FDG implementation, representing the duration of the game. Each top level team will receive half of this duration on their initial chess clocks. -## Game Mechanics +## Core Game Mechanics + +This section specifies the core game mechanics of the FDG. The full FDG mechanics includes a +[specification for Bonds](./bond-incentives.md). Readers should understand basic game mechanics before +reading up on the Bond specification. ### Actors diff --git a/specs/experimental/fault-proof/honest-challenger-fdg.md b/specs/experimental/fault-proof/honest-challenger-fdg.md index b93d1d66c..f94c77ebc 100644 --- a/specs/experimental/fault-proof/honest-challenger-fdg.md +++ b/specs/experimental/fault-proof/honest-challenger-fdg.md @@ -50,7 +50,7 @@ to its root claim: NOTE: The honest challenger will still track this game in order to defend any subsequent claims made against the root claim - in effect, "playing the game". -### Counter Claims +### Countering Invalid Claims For every claim made in a dispute game with a [game tree](fault-dispute-game.md#game-tree) depth in the range of `[1, MAX_DEPTH]`, the honest challenger processes them and performs @@ -62,7 +62,8 @@ This determines the set of claims it should respond to in the FDG. If the agent determines itself to be a Defender, which aims to support the root claim, then it must dispute claims positioned at odd depths in the game tree. Otherwise, it disputes claims positioned at even depths in the game tree. -This means an honest challenger only responds to claims made by the opposing team. +This means an honest challenger will typically only respond to claims made by the opposing team. +(See [Countering Freeloaders](#countering-freeloaders) for exceptions to this). The next step is to determine if the claim, now known to be for the opposing team, disputes another claim the honest challenger _agrees_ with. @@ -77,6 +78,23 @@ If the `ClaimHash` matches the honest challenger's at the same trace index, then disagree with the claim's stance by moving to [defend](fault-dispute-game.md#defend). Otherwise, the claim is [attacked](fault-dispute-game.md#attack). +### Countering Freeloaders + +Freeloaders are claims that exist at the correct depth and on the same team as honest challengers +but are positioned incorrectly or commit to an invalid `ClaimHash`. +The honest challenger must dispute freeloaders to claim the bond of the subgame root. +If not disputed, the bond may be awarded to the freeloader, depending on their position. +See [Bond incentives for subgame Resolution](./bond-incentives.md) for details. + +The honest challenger achieves this by disputing any freeloader claim that is not invalidly positioned +in a defensive position. This includes disputing the following types of claims: + +- Claims at an invalid attack position +- Claims with an invalid `ClaimHash` at a valid attack position +- Claims with a valid `ClaimHash` at a valid defense position + +Doing so ensures that the leftmost claim is the hoenst challenger's. + The following pseudocode illustrates the response logic. ```python @@ -89,24 +107,44 @@ class Claim: position: uint64 claim_hash: ClaimHash +class Response(Enum): + ATTACK = 0 + DEFEND = 1 + NOP = 2 + MAX_TRACE = 2**MAX_GAME_DEPTH -def agree_with(claim: Claim, chal_trace: List[ClaimHash, MAX_TRACE]): - if chal_trace[claim.trace_index] != claim.claim_hash: +def agree_with(claim: Claim, chal_trace: List[ClaimHash, MAX_TRACE]) -> bool: + if chal_trace[trace_index(claim.position)] != claim.claim_hash: return False grand_parent = claim.parent.parent if claim.parent is not None else None if grand_parent is not None: return agree_with(grand_parent) return True -def respond(claim: Claim, chal: Team, chal_trace: List[ClaimHash, MAX_TRACE]): +def is_attack(claim: Claim) -> bool: + return claim.position == claim.parent.position << 1 + +def respond_claim(claim: Claim, correct_trace: List[ClaimHash, MAX_TRACE]) -> Response: + if chal_trace[trace_index(claim.position)] == claim.claim_hash: + return Response.DEFEND + else: + return Response.ATTACK + +def respond(claim: Claim, chal: Team, chal_trace: List[ClaimHash, MAX_TRACE]) -> Response: if depth(claim.position) % 2 != chal.value: if claim.parent is None or agree_with(claim.parent, chal_trace): - if chal_trace[trace_index(claim.position)] == claim.claim_hash: - defend() - else: - attack() - else: pass # avoid supporting invalid claims on the same team + return respond_claim(claim, chal_trace) + else: + return Response.NOP # avoid supporting invalid claims on the same team + else: + correct_response = respond(claim.parent, chal, chal_trace) + claim_response = Response.ATTACK if is_attack(claim) else Response.DEFEND + invalid_defense = claim_response == Response.DEFEND and correct_response == Respond.ATTACK + if not invalid_defense: + return respond_claim(claim, chal_trace) + else: + return Response.NOP ``` In attack or defense, the honest challenger submit a `ClaimHash` corresponding to the