7a1346f4ba5a550992da84d5da88d403f994fd88 — Edd Salkield 1 year, 4 months ago 2c13f59
projects: add protecol
1 files changed, 145 insertions(+), 0 deletions(-)

A content/projects/protecol.md
A content/projects/protecol.md => content/projects/protecol.md +145 -0
@@ 0,0 1,145 @@
title: "Protécol -- Zero-Trust Peer-to-Peer Pokémon Battle Protocol"
date: 2023-01-16
    tags: ["idea"]

Pokémon battles are a partial-knowledge nondeterministic decidable turn-taking game.
They're also great fun!
Online battle simulations such as [Showdown](https://github.com/smogon/pokemon-showdown) use a third-party server to enforce the rules: deciding which moves are valid, which random numbers are generated, and which player wins[^0].
But a malicious third player could skew the odds against you, and an unreliable third party might not always be available to let you play -- a peer-to-peer protocol would intead let two parties battle directly with each other without relying on a third party to enforce the rules.
It would also let you play the game asynchronously, for example by email or [avian carrier](https://datatracker.ietf.org/doc/html/rfc1149).

But without a third party, what's to stop your opponent from cheating?
Answer: a cryptographic protocol!

# Properties of the game

Let's unpack the game of Pokémon a bit.
Two players who compete against each other by battling Pokémon against each other.
Each player plays one Pokémon each into the field, which can then play one of four possible moves, affecting the status and/or HP (Health Points) of their opponent's Pokémon.
A player wins if their opponent's Pokémon faint first[^1].

## Partial information

Information is partial - for example, the Pokémon in your opponent's team, their move sets, and max HP are not revealed to you at the start of the game.
This lets your opponent cheat by not actually deciding upon their team at the start, but choosing as they go along based on what you've played before.
[Absurdle](https://qntm.org/files/absurdle/absurdle.html), an adversarial version of Wordle, demonstrates this concept well.
We can use a _commitment scheme_ to solve this, so each player is forced to decide upon their team beforehand.

Moves are taken together, meaning that each player decides upon a move before the turn starts.
This could let your opponent wait until you've decided upon your move before deciding upon theirs.
We therefore need another commitment scheme, so that each move is made independently without knowledge of your opponent's move.

## Nondeterminism

Some moves are nondeterministic - whether the move succeeds or the amount of damage it inflicts is decided by a random factor.
This lets your opponent cheat by choosing or influencing the generated numbers to suit them, causingyour moves to miss more often.
We can use a _distributed random number generation algorithm_ to solve this, where the number is guaranteed to be random even if your opponent chooses the number.

## Verifiability

Finally, games should be verifiable and non-repudiable - this means that if a third party wants to check who won, you can't pretend that you won or claim you didn't play at all.

So how do we enforce these properties?

# Channel security

Firstly, the two players need to be able to communicate securely.
This means that each player knows that they're talking the the other player that they're intending to battle - authenticity.
They also need to know that the moves they make aren't being tampered with en route -- integrity.

We can solve this using any number of channel-securing protocols over the internet, such as TLS.
This relies upon each player having an asymmetric key pair, with a public and private key each.
We could alternatively implement this by signing messages using a tool like [GnuPG](https://gnupg.org/) or [age](https://github.com/FiloSottile/age).
We're going to assume from here on that all communications happen in such a channel.

Within the channel, each move in the game needs to be signed by each player's private key - this allows third-parties to verify the identify of the players involved in the game to generate leaderboards etc.
A one-time session key should therefore be generated by each player at the beginning of the game, preventing cryptanalysis of their key by the repeated playing of games.

# Verifying move correctness

At the start of the game, neither player knows which Pokémon the other has, nor which moves they are capable of.
This makes it difficult to check during the game whether a certain move is a valid play.
We therefore use a commitment scheme to make each player commit to a certain Pokémon team at the beginning, without revealing any information about the team.
At the end of the game, it can be checked that the only moves that were played are those committed to at the start.

This is done by publishing a _hash_ of the team and a _n-once_ before the game starts.
Since this hash is unique to the team, it can be verified at the end of the game that the moves that were made were part of the initial team.
Because the n-once (a number used only once) is inside the hash, the opponent cannot reverse the hash by brute-forcing every team combination.
If the channel were not secured, the hash would have to be keyed.

Certain optimisations can be made to check obedience to the rules - the opponent is guaranteed to be cheating if they use a move that their particular Pokémon cannot play, or if one of their Pokémon plays more than four types of move in a game[^2].

# Verifying move outcomes

It's also hard to check what the valid outcome of a move should be.
Even for simple moves that only inflict damage, the amount of damage inflicted depends on the stats of both the attacking and defending Pokémon, both of which are initially secret.
The formula for calculating damage (in Gen 1 games) is as follows:


The important variables are _A_ and _D_ - the effective attack and defence stats of the Pokémon involved.
The challenges in generating the random number _random_ are discussed in section _Nondeterministic moves_.

All other parts of the formula are public knowledge, and therefore known to each party.
Therefore, once the damage is calculated, each player can simply rearrange the formula to figure out their opponent's attack or defence stat.
As a result, there's no point trying to keep these stats secret when a move is made - they should instead be communicated, so that each player can calculate the damage.

# Verifying move ordering

Moves are made in a certain order, according to several factors including the attacking Pokémon's speed statistic.
The speed of a Pokémon is initially secret, although an estimation of it can be inferred by observing the order of play.
Therefore, we need to be able to compare which move was the fastest, without revealing the precise speed stat of each Pokémon.

This is an instance of _Yao's Millionaires' problem_, and can be solved by a number of algorithms.
The solution presented by Hsiao-Ying Lin and Wen-Guey Tzeng depends on the idea of constructing two sets, one for each number a and b, where the sets have a common element if and only if a > b[^3].

# Nondeterministic moves

The damage that Pokémon move inflicts, alongside whether it succeeds at all, are determined partially at random.
However, neither player can trust their opponent to generate a truly random number, since the outcome of the move can be influenced by choosing a good number.

We therefore employ a _distributed random number generation algorithm_.
In this algorithm, each player _A_ and _B_ commits a number, _n<sub>a</sub>_ and _n<sub>b</sub>_ respectively.
They commit the number by publishing its hash.
Then once both commitments have been made, each player reveals their number, and computes

_x = n<sub>a</sub> ⊕ n<sub>b</sub>_

where ⊕ is bitwise XOR.

_x<sub>n</sub>_ can be used as a seed in a PRNG[^4] to generate a number in the expected range.

As Awerbuch and Scheideler note in _Robust Random Number Generation for Peer-to-Peer Systems_[^5], this is a secure algorithm:

> The XOR operation has the nice property that as long as at least one [number] is chosen uniformly at random and the other numbers are independent of it, x is distributed uniformly at random

However, they note that adversaries can wait for their opponent to reveal a number, and choose not to reveal their own.
If the protocol can be made to restart, they can bias the game by only accepting advantageous numbers.
This isn't a problem in Pokémon, since to not reveal a number halts the entire game, and this is our intended outcome in the case of tampering.

# Conclusion

We've presented a zero-trust peer-to-peer pokémon battle protocol which implements the basics of Pokémon battle mechanics.
However, I haven't made a proof of concept or attempted to formalise or verify the correctness of this protocol.

There's still a lot left to think about, too.
Supporting doubles battles will require changes to the commitment schemes to support more players.
Implementation details, such as designing clients to show all calculable statistics, will be important.
It would be cool to see if an implementation of this protocol could be interfaced with devices that can't do the crypto - such as supporting battles on real-world copies of GameBoy games through the link cable.
It would also be interesting to consider anti-forfeiting schemes, where a third party could do liveness checks - if a player doesn't sign a move in a certain period of time, the game can be considered forfeit.

# Further work
Doubles battling (or n-way)
Third-party verifiability
Interfacing with Pokemon clients that can't do crypto - using a trusted party

[^0]: They do have a [pretty neat protocol](https://github.com/smogon/pokemon-showdown/blob/master/PROTOCOL.md), though.
[^1]: Moves have a total ordering within the turn, and their effects are applied sequentially. This means that it's always possible to determine which Pokémon moves, and therefore faints, first.
[^2]: I don't know how evolutions, and therefore moveset changes, work in competitive battles.
[^3]: [https://en.wikipedia.org/wiki/Yao%27s_Millionaires%27_problem](https://en.wikipedia.org/wiki/Yao%27s_Millionaires%27_problem)
[^4]: Pseudo-Random Number Generator
[^5]: [http://wwwmayr.informatik.tu-muenchen.de/personen/scheideler/papers/OPODIS-116b.pdf](http://wwwmayr.informatik.tu-muenchen.de/personen/scheideler/papers/OPODIS-116b.pdf)