Try   HackMD

FHE FrogCrypto arena

Table of contents

This document outlines the design of an FHE-based crypto-frog arena. The idea is that we can use the POD-ed version of FrogCrypto to actually use the frogs to battle against eachother!

YOU CAN TEST THE ARENA HERE: https://github.com/CPerezz/phantom-zone/blob/frogcrypto_arena_example/examples/frog_arena.rs

To run:

$ cargo run --release --example frog_arena --features "non_interactive_mp"

But with a few catches:

  • Players can't learn the teams (frogs) that other players brought to the battle.
  • Players can't learn in which order will each of their oponent's frogs fought.

Future ideas:

  • (Sign a POD about victory or lose) once the battle is over (That allows to build ELO/rankings)
  • Betting can become a thing.

General architecture

Frog PODs

A Frog inside of FrogCrypto is assumed to have at least the following stats:

  • Attack: u8 -> Defines the raw damage is capable of causing.
  • Defence: u8 -> Defines the raw damage that the frog is capable to mitigate.
  • Health: u8 -> Ammount of hitpoints that a frog can take prior to fainting.
  • Type: u8 -> Class of attack that a frog has. Notice that the Types allow for multiple multipliers being applied to frog's stats.
  • Speed: u8 -> Defines which of the frogs will attack first.

These POD-ed frogs are cryptographically signed data owned by each person. They can actually make proofs about them such that the FHE server can authenticate the teams and proceed to celebrate the match.

Attack:

Attack is a quite simple stat. It should be always kept between 0-50 (such that we have some max scale and we don't do non-sensical stats).

Defence:

Defense should also be quite simple. It can never be

the maximum attack allowed (50).

Speed

Speed will just decide which frog attacks first. Such that in this way we allow for more elaborated strategies and richer play.

Notice that introducing this concept, we already introduce branching within the FHE arena circuit. This implies we will have to be careful on what refers to performance.

Type

Here is the truth table of types and the boolean expression it results into (such that types can apply modifiers):

We define the truth table:

Types Fire Water Plant
F F F F
Fire F F F T
Water F T F F
Plant F F T F

Notice that the table aims to have the minimal amount of types such that they're all balanced.
Hence, we have one empty row and column since we have 3 types and therefore we need 2 bits wich allows for

22 types. So we reject one by simply setting it all to false and never assigning it's ID to any frog.

Then, we perform the Karnough map over the truth table to obtain a boolean expression:

f(a, b, c, d) = a'cd + b'c' + abd'
f <= (not a and c and d) or (not b and not c) or (a and b and not d);

The expression is already simplified. So that's the minimum we will need to work with.

If the truth table expression is run, and one of the frogs obtains a 1 as an output, it's attack will get +5/+10.

This is not ideal. But we want to avoid multiplications for now at all costs as will imply even more bootstrapping operations.

Health

Although health is also a very simple stat. It going it to 0 or wrapping up over it causes a frog to faint and so, the battle to finish.
Health should be a parameter that is at least

Max Attack such that we have at least more than 1 round on each fight.

The arena

The arena circuit is the one which is going to take care of carrying over the fight of the frogs.
For it to work, we need several functions and structs:

We will omit speed and type for now for simplicity

// If `true`, Frog1 attacks first, otherwise, Frog2 does. fn select_first_attacker(f1: Frog, f2: Frog) -> bool { let f1_geq_f2_speed = f1.speed().geq(f2.speed()); conditionally_select_1(f1_geq_f2_speed, 0) }

Basic Frog structure definition

/// This is the definition of the Frog Stats valid for FHE/Non-FHE worlds struct FrogStats<T> { attack: T, defense: T, health: T, }

Round damage computation

fn compute_round_damage_fhe( f1: &FrogStats<FheUint8>, f2: &FrogStats<FheUint8>, ) -> (FheBool, FheBool, FheBool, FheUint8, FheUint8) { // We first compute damages. let f1_damage_f2 = f1.attack.sub(&f2.defense); let f2_damage_f1 = f2.attack.sub(&f1.defense); // We check for possible faints. let f1_faints: FheBool = f2_damage_f1.ge(&f1.health); let f2_faints = f1_damage_f2.ge(&f2.health); // If both faint, result is a draw. let draw = &f1_faints & &f2_faints; let f2_wins = &f1_faints & &(!(&f2_faints)); let f1_wins = &f2_faints & &(!(&f1_faints)); (draw, f2_wins, f1_wins, f1_damage_f2, f2_damage_f1) }

FHE Battle main function

fn fhe_battle( f1: &mut FrogStats<FheUint8>, f2: &mut FrogStats<FheUint8>, zero: FheUint8, ) -> (FheBool, FheBool, FheBool) { let mut round_finished = !(&zero.eq(&zero)); let mut is_draw = round_finished.clone(); let mut is_f1_win = round_finished.clone(); let mut is_f2_win = round_finished.clone(); for _ in 0..NUM_ROUNDS { let (draw, f2_wins, f1_wins, f1_damage_f2, f2_damage_f1) = compute_round_damage_fhe(&f1, &f2); // We update the results vector depending on the result of `frogs_fainted`. // If a frog fainted this fight, we update `round_finished` and simply cancel // out any new updates to any of the fields. is_draw |= &draw & &(!(&round_finished)); is_f1_win |= &f1_wins & &(!(&round_finished)); is_f2_win |= &f2_wins & &(!(&round_finished)); let frogs_fainted = &(&(&draw | &f2_wins) | &f1_wins) | &round_finished; round_finished |= frogs_fainted.clone(); // We need to update frog's health to the new ones if none of them fainted this // round. let neg_frogs_fainted = !(&frogs_fainted); let f1_damage_f2_applied = &f2.health - &f1_damage_f2; let f2_damage_f1_applied = &f1.health - &f2_damage_f1; // We apply damage and go for the next round. *f1.health_mut() = f2_damage_f1_applied.mux(&f1.health, &neg_frogs_fainted); *f2.health_mut() = f1_damage_f2_applied.mux(&f2.health, &neg_frogs_fainted); } (is_draw, is_f1_win, is_f2_win) }
  • Lots of conditions = Lots of execution path to traverse = Performance kill.
  • Loops need to be unrolled

Battle ended in round 2 but we need to run Round 3 and cancel out all state updates based on that.

  • We can parallelize within rounds but not across rounds.

Now what?

  • Well.. Secret team FHE arena fight is live.
  • Pod Issuance based on wins.
  • Is possible to add guesses on the enemy team and decrypt only if they were right.
  • Multiple frogs + Types can start to be fun.

We can go crazy

  • Each player encrypts a signed ETH transaction with X ETH inside that only the winner is going to be able to claim. => P2P casino-style of things.

Further applications for the same idea:

  • Performance based bonuses based on GH commits/reviews.
    Company encrypts signed ETH tx and if the employee can prove with PODs the ammount of commits to a repo + reviews then is able to claim the bonus.