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:
Future ideas:
A Frog inside of FrogCrypto is assumed to have at least the following stats:
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 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).
Defense should also be quite simple. It can never be
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.
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
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.
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
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)
}
Battle ended in round 2 but we need to run Round 3 and cancel out all state updates based on that.
We can go crazy
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.