Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create two_phase_game_move_execution.md #2117

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions design/ux/two_phase_game_move_execution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Two-Phase Game Move Execution

Games running on platforms with Smart Transparency and native RNG are vulnerable to a couple of attacks:

### 1. The Proxy execute-revert attack

The attacker deploys a contract that acts as a proxy between the user and the game contract.
The proxy calls the `move` function of the game. Then it queries the result by invoking a view function on the game.
If it doesn't like the result it just reverts.

This attack can be made smarter by the proxy trying moves in a loop until it wins the prize.


### 2. Gas estimate side-channel attack

The attacker calls the free gas estimate for different moves. `Gas estimate` does not normally return the result and does not modify the state.
But, if the game has a slight difference between the execution paths of winning and losing, then the attacker could infer from the gas used whether the simulated move is a winner.


## Proposed solution - Two-Phase execution

This solution combines a pattern that game developers must implement with some primitives offered by the platform.

### Game pattern - Decouple move recording from move execution

When the user interacts with a game, they just commit to a move which the game stores in an internal queue. No processing should happen in this first phase.
The execution will be performed in phase 2 - as a separate transaction.

Note: The "execution" transaction can't be triggered by the user because then they can perform the proxy attack by executing both phases inside the proxy.

To implement this pattern securely, there must be a trusted third party that calls "execute" on behalf of users.
This solution is not ideal because it affects the UX - the user will have to wait at least 2 blocks for a response, and it introduces a third party.
There is also the issue of the execution cost. The third party has to be somehow paid for this service.


### Platform support - execution callback

TEN already has a system contract that is executed at the end of every block. The execution of the sys contract is triggered by the platform as a "synthetic" transaction.

If games had a way to register themselves with this system contract, then it could call the execution phase automatically at the end of each batch.

This solves the UX problem - because the user would get the response at the same time, and it also removes the need for a trusted third party.

#### Implementation

Ideally, the registration of the callback would be a "smart contract"-only action.
Basically, the system contract that is called automatically will have a function where it allows anyone to register a callback.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add all this to the current contract, but it needs to be a different function with a different synthetic transaction that is potentially metered. Current callback passes all transactions for inspection which ruins privacy; We need a callback where we register data to callback and address, along with gas maybe?
So that for devs the experience can be
system.registerCallback(abi.encodeCall(thisContract.execute.selector, arg1, arg2));


#### Paying for the execution

This is the trickiest component of the solution.

On a high level, the most likely requirement is that the Game would ask the users to pay an amount that covers both the recording of the move and the execution of an average move.
As part of the "phase 1" transaction, the money for the move recording goes to the platform, while the estimated money for "phase 2" goes to an account owned by the game.

Assuming we implement the callback as described in the previous section, we need a way from solidity to calculate the gas cost of executing each move.
This appears to be possible using a workaround with the `gasLeft()` function.

##### Possible Implementation of execution payment

1. The game developer has to estimate how much an average move will cost to execute and add some margin.
2. During the "phase 1" transaction, the game dev will transfer some `value` from the caller to an address controlled by the sys contract.
This address was allocated to this game during the callback registration (similar to how you have to reference a payment when you move money to some broker).
Note: To the user, it will be equivalent to gas spent for the transaction.
4. At the end of the batch, the sys contract will invoke the "phase 2" function of the game - one move at a time.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'll say pretty much what's in the PR already, but just to be clear - system contract will invoke registered callbacks in order of registration; so we can think of it as a queue. registerCallback pushes in it, and then we pop. So if there is some ordering where contract A registers callback and then calls contract B in same transaction which registers another, then it comes back and third one is registered it should all be executed in order. Not sure if this will ever be of any practical use, but I think this is the intuitive expectation

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

umm. We need to think about this a bit more.

It will meter the cost of each move, and check whether there is enough left in the prepaid account to continue.