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

Add zk fault proof contracts #258

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open

Add zk fault proof contracts #258

wants to merge 16 commits into from

Conversation

redhdx
Copy link
Contributor

@redhdx redhdx commented Jan 13, 2025

Description

add a description of your changes here...

Rationale

tell us why we need these changes...

Example

add an example CLI or API response...

Changes

Notable changes:

  • add each change in a bullet point here
  • ...

Comment on lines +151 to +178
function _validateGameCreation(
GameType _gameType,
Claim[] calldata _claims,
uint64 _parentGameIndex,
IDisputeGame impl
) private view returns (IDisputeGame parentProxy) {
// If there is no implementation to clone for the given `GameType`, revert.
if (address(impl) == address(0)) revert NoImplementation(_gameType);
// If the required initialization bond is not met, revert.
if (msg.value != initBonds[_gameType]) revert IncorrectBondAmount();
if (_claims.length == 0) revert NoClaims();
// When the parent game index is the maximum value of a uint64, it means that there is no parent game.
// And use the state from anchor state registry.
if (_parentGameIndex != type(uint64).max && _parentGameIndex >= _disputeGameList.length) {
revert InvalidParentGameIndex();
}
if (_parentGameIndex != type(uint64).max) {
(GameType parentGameType, , address proxy) = _disputeGameList[_parentGameIndex].unpack();
parentProxy = IDisputeGame(proxy);
// The parent game must be of the same type as the child game.
if (parentGameType.raw() != _gameType.raw()) revert InvalidParentGameType();
if (parentProxy.status() == GameStatus.CHALLENGER_WINS) {
revert InvalidParentGameStatus();
}
} else {
parentProxy = IDisputeGame(address(0));
}
}
Comment on lines 382 to 468
function resolve() external returns (GameStatus status_) {
// INVARIANT: Resolution cannot occur unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
GameStatus parentStatus = parentGameStatus();
// parent game must be resolved
if (parentStatus == GameStatus.IN_PROGRESS) {
revert ParentGameNotResolved();
}
if (parentStatus == GameStatus.CHALLENGER_WINS) {
status_ = GameStatus.CHALLENGER_WINS;
// re-update fault proof prover
faultProofProver = payable(parentGameProxy().gameWinner());
}
if (isChallengeSuccess) {
status_ = GameStatus.CHALLENGER_WINS;
}

if (status_ != GameStatus.CHALLENGER_WINS) {
// first check if the challenge window is expired
if (block.timestamp - createdAt.raw() <= MAX_CLOCK_DURATION.raw()) {
revert ClockNotExpired();
}
// then check if there is any remaining challenges
for (uint256 i = 0; i < challengedClaimIndexes.length; i++) {
if (!invalidChallengeClaims[challengedClaimIndexes[i]]) {
revert UnresolvedChallenges();
}
}
status_ = GameStatus.DEFENDER_WINS;
}

uint256 currentContractBalance = WETH.balanceOf(address(this));
if (status_ == GameStatus.CHALLENGER_WINS) {
// refund valid challengers if there is any
for (uint256 i = 0; i < challengedClaimIndexes.length; i++) {
if (!invalidChallengeClaims[challengedClaimIndexes[i]]) {
// refund the bond
challengers[challengedClaimIndexes[i]].transfer(CHALLENGER_BOND);
}
}
// TODO reward part of challengers bond to validity provers, current reward is zero
// reward the special challenger who submitted the signal which is proven to be valid
// 1. someone submitted a valid fault proof corresponding to the challenge index; or
// 2. the generate proof window is expired and no one submitted a validity proof
// If isChallengeSuccess is true, then the challenger exists;
// If isChallengeSuccess is false, then it indicates that the parent game is CHALLENGER_WINS and
// there is no successful challenge in the current game.
if (isChallengeSuccess) {
// there is a challenger who submmitted the dispute claim index by `challengeBySignal`
uint256 challengerBond = (currentContractBalance * CHALLENGER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
if (challengedClaims[successfulChallengeIndex]) {
_distributeBond(challengers[successfulChallengeIndex], challengerBond);
} else {
// if there is no challenger, then the challenger is the fault proof prover self
_distributeBond(faultProofProver, challengerBond);
}
currentContractBalance = currentContractBalance - challengerBond;
}
// reward the fault proof prover
uint256 proverBond = (currentContractBalance * PROVER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
_distributeBond(faultProofProver, proverBond);
currentContractBalance = currentContractBalance - proverBond;
} else if (status_ == GameStatus.DEFENDER_WINS) {
// reward part of challengers bond to validity provers
for (uint256 i = 0; i < invalidChallengeClaimIndexes.length; i++) {
uint256 proverBond = (CHALLENGER_BOND * PROVER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
_distributeBond(validityProofProvers[invalidChallengeClaimIndexes[i]], proverBond);
currentContractBalance = currentContractBalance - proverBond;
}
// refund the bond to proposer
_distributeBond(gameCreator(), PROPOSER_BOND);
currentContractBalance = currentContractBalance - PROPOSER_BOND;
} else {
// sanity check
revert InvalidGameStatus();
}
// transfer the rest
_distributeBond(FEE_VAULT_ADDRESS, currentContractBalance);

resolvedAt = Timestamp.wrap(uint64(block.timestamp));

// Update the status and emit the resolved event, note that we're performing an assignment here.
emit Resolved(status = status_);

// Try to update the anchor state, this should not revert.
ANCHOR_STATE_REGISTRY.tryUpdateAnchorState();
}
Comment on lines 383 to 471
function resolve() external returns (GameStatus status_) {
// INVARIANT: Resolution cannot occur unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
GameStatus parentStatus = parentGameStatus();
// parent game must be resolved
if (parentStatus == GameStatus.IN_PROGRESS) {
revert ParentGameNotResolved();
}
if (parentStatus == GameStatus.CHALLENGER_WINS) {
status_ = GameStatus.CHALLENGER_WINS;
// re-update fault proof prover
faultProofProver = payable(parentGameProxy().gameWinner());
}
if (isChallengeSuccess) {
status_ = GameStatus.CHALLENGER_WINS;
}

if (status_ != GameStatus.CHALLENGER_WINS) {
// first check if the challenge window is expired
if (block.timestamp - createdAt.raw() <= MAX_CLOCK_DURATION.raw()) {
revert ClockNotExpired();
}
// then check if there is any remaining challenges
for (uint256 i = 0; i < challengedClaimIndexes.length; i++) {
if (!invalidChallengeClaims[challengedClaimIndexes[i]]) {
revert UnresolvedChallenges();
}
}
status_ = GameStatus.DEFENDER_WINS;
}

uint256 currentContractBalance = WETH.balanceOf(address(this));
if (status_ == GameStatus.CHALLENGER_WINS) {
// refund valid challengers if there is any
for (uint256 i = 0; i < challengedClaimIndexes.length; i++) {
if (!invalidChallengeClaims[challengedClaimIndexes[i]]) {
// refund the bond
_distributeBond(challengers[challengedClaimIndexes[i]], CHALLENGER_BOND);
currentContractBalance = currentContractBalance - CHALLENGER_BOND;
}
}
// TODO reward part of challengers bond to validity provers, current reward is zero
uint256 initialBalance = currentContractBalance;
// reward the special challenger who submitted the signal which is proven to be valid
// 1. someone submitted a valid fault proof corresponding to the challenge index; or
// 2. the generate proof window is expired and no one submitted a validity proof
// If isChallengeSuccess is true, then the challenger exists;
// If isChallengeSuccess is false, then it indicates that the parent game is CHALLENGER_WINS and
// there is no successful challenge in the current game.
if (isChallengeSuccess) {
// there is a challenger who submmitted the dispute claim index by `challengeBySignal`
uint256 challengerBond = (currentContractBalance * CHALLENGER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
if (challengedClaims[successfulChallengeIndex]) {
_distributeBond(challengers[successfulChallengeIndex], challengerBond);
} else {
// if there is no challenger, then the challenger is the fault proof prover self
_distributeBond(faultProofProver, challengerBond);
}
currentContractBalance = currentContractBalance - challengerBond;
}
// reward the fault proof prover
uint256 proverBond = (initialBalance * PROVER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
_distributeBond(faultProofProver, proverBond);
currentContractBalance = currentContractBalance - proverBond;
} else if (status_ == GameStatus.DEFENDER_WINS) {
// reward part of challengers bond to validity provers
for (uint256 i = 0; i < invalidChallengeClaimIndexes.length; i++) {
uint256 proverBond = (CHALLENGER_BOND * PROVER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
_distributeBond(validityProofProvers[invalidChallengeClaimIndexes[i]], proverBond);
currentContractBalance = currentContractBalance - proverBond;
}
// refund the bond to proposer
_distributeBond(gameCreator(), PROPOSER_BOND);
currentContractBalance = currentContractBalance - PROPOSER_BOND;
} else {
// sanity check
revert InvalidGameStatus();
}
// transfer the rest
_distributeBond(FEE_VAULT_ADDRESS, currentContractBalance);

resolvedAt = Timestamp.wrap(uint64(block.timestamp));

// Update the status and emit the resolved event, note that we're performing an assignment here.
emit Resolved(status = status_);

// Try to update the anchor state, this should not revert.
ANCHOR_STATE_REGISTRY.tryUpdateAnchorState();
}

Check warning

Code scanning / Slither

Reentrancy vulnerabilities Medium

Comment on lines 383 to 471
function resolve() external returns (GameStatus status_) {
// INVARIANT: Resolution cannot occur unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
GameStatus parentStatus = parentGameStatus();
// parent game must be resolved
if (parentStatus == GameStatus.IN_PROGRESS) {
revert ParentGameNotResolved();
}
if (parentStatus == GameStatus.CHALLENGER_WINS) {
status_ = GameStatus.CHALLENGER_WINS;
// re-update fault proof prover
faultProofProver = payable(parentGameProxy().gameWinner());
}
if (isChallengeSuccess) {
status_ = GameStatus.CHALLENGER_WINS;
}

if (status_ != GameStatus.CHALLENGER_WINS) {
// first check if the challenge window is expired
if (block.timestamp - createdAt.raw() <= MAX_CLOCK_DURATION.raw()) {
revert ClockNotExpired();
}
// then check if there is any remaining challenges
for (uint256 i = 0; i < challengedClaimIndexes.length; i++) {
if (!invalidChallengeClaims[challengedClaimIndexes[i]]) {
revert UnresolvedChallenges();
}
}
status_ = GameStatus.DEFENDER_WINS;
}

uint256 currentContractBalance = WETH.balanceOf(address(this));
if (status_ == GameStatus.CHALLENGER_WINS) {
// refund valid challengers if there is any
for (uint256 i = 0; i < challengedClaimIndexes.length; i++) {
if (!invalidChallengeClaims[challengedClaimIndexes[i]]) {
// refund the bond
_distributeBond(challengers[challengedClaimIndexes[i]], CHALLENGER_BOND);
currentContractBalance = currentContractBalance - CHALLENGER_BOND;
}
}
// TODO reward part of challengers bond to validity provers, current reward is zero
uint256 initialBalance = currentContractBalance;
// reward the special challenger who submitted the signal which is proven to be valid
// 1. someone submitted a valid fault proof corresponding to the challenge index; or
// 2. the generate proof window is expired and no one submitted a validity proof
// If isChallengeSuccess is true, then the challenger exists;
// If isChallengeSuccess is false, then it indicates that the parent game is CHALLENGER_WINS and
// there is no successful challenge in the current game.
if (isChallengeSuccess) {
// there is a challenger who submmitted the dispute claim index by `challengeBySignal`
uint256 challengerBond = (currentContractBalance * CHALLENGER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
if (challengedClaims[successfulChallengeIndex]) {
_distributeBond(challengers[successfulChallengeIndex], challengerBond);
} else {
// if there is no challenger, then the challenger is the fault proof prover self
_distributeBond(faultProofProver, challengerBond);
}
currentContractBalance = currentContractBalance - challengerBond;
}
// reward the fault proof prover
uint256 proverBond = (initialBalance * PROVER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
_distributeBond(faultProofProver, proverBond);
currentContractBalance = currentContractBalance - proverBond;
} else if (status_ == GameStatus.DEFENDER_WINS) {
// reward part of challengers bond to validity provers
for (uint256 i = 0; i < invalidChallengeClaimIndexes.length; i++) {
uint256 proverBond = (CHALLENGER_BOND * PROVER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
_distributeBond(validityProofProvers[invalidChallengeClaimIndexes[i]], proverBond);
currentContractBalance = currentContractBalance - proverBond;
}
// refund the bond to proposer
_distributeBond(gameCreator(), PROPOSER_BOND);
currentContractBalance = currentContractBalance - PROPOSER_BOND;
} else {
// sanity check
revert InvalidGameStatus();
}
// transfer the rest
_distributeBond(FEE_VAULT_ADDRESS, currentContractBalance);

resolvedAt = Timestamp.wrap(uint64(block.timestamp));

// Update the status and emit the resolved event, note that we're performing an assignment here.
emit Resolved(status = status_);

// Try to update the anchor state, this should not revert.
ANCHOR_STATE_REGISTRY.tryUpdateAnchorState();
}
Comment on lines 383 to 471
function resolve() external returns (GameStatus status_) {
// INVARIANT: Resolution cannot occur unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
GameStatus parentStatus = parentGameStatus();
// parent game must be resolved
if (parentStatus == GameStatus.IN_PROGRESS) {
revert ParentGameNotResolved();
}
if (parentStatus == GameStatus.CHALLENGER_WINS) {
status_ = GameStatus.CHALLENGER_WINS;
// re-update fault proof prover
faultProofProver = payable(parentGameProxy().gameWinner());
}
if (isChallengeSuccess) {
status_ = GameStatus.CHALLENGER_WINS;
}

if (status_ != GameStatus.CHALLENGER_WINS) {
// first check if the challenge window is expired
if (block.timestamp - createdAt.raw() <= MAX_CLOCK_DURATION.raw()) {
revert ClockNotExpired();
}
// then check if there is any remaining challenges
for (uint256 i = 0; i < challengedClaimIndexes.length; i++) {
if (!invalidChallengeClaims[challengedClaimIndexes[i]]) {
revert UnresolvedChallenges();
}
}
status_ = GameStatus.DEFENDER_WINS;
}

uint256 currentContractBalance = WETH.balanceOf(address(this));
if (status_ == GameStatus.CHALLENGER_WINS) {
// refund valid challengers if there is any
for (uint256 i = 0; i < challengedClaimIndexes.length; i++) {
if (!invalidChallengeClaims[challengedClaimIndexes[i]]) {
// refund the bond
_distributeBond(challengers[challengedClaimIndexes[i]], CHALLENGER_BOND);
currentContractBalance = currentContractBalance - CHALLENGER_BOND;
}
}
// TODO reward part of challengers bond to validity provers, current reward is zero
uint256 initialBalance = currentContractBalance;
// reward the special challenger who submitted the signal which is proven to be valid
// 1. someone submitted a valid fault proof corresponding to the challenge index; or
// 2. the generate proof window is expired and no one submitted a validity proof
// If isChallengeSuccess is true, then the challenger exists;
// If isChallengeSuccess is false, then it indicates that the parent game is CHALLENGER_WINS and
// there is no successful challenge in the current game.
if (isChallengeSuccess) {
// there is a challenger who submmitted the dispute claim index by `challengeBySignal`
uint256 challengerBond = (currentContractBalance * CHALLENGER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
if (challengedClaims[successfulChallengeIndex]) {
_distributeBond(challengers[successfulChallengeIndex], challengerBond);
} else {
// if there is no challenger, then the challenger is the fault proof prover self
_distributeBond(faultProofProver, challengerBond);
}
currentContractBalance = currentContractBalance - challengerBond;
}
// reward the fault proof prover
uint256 proverBond = (initialBalance * PROVER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
_distributeBond(faultProofProver, proverBond);
currentContractBalance = currentContractBalance - proverBond;
} else if (status_ == GameStatus.DEFENDER_WINS) {
// reward part of challengers bond to validity provers
for (uint256 i = 0; i < invalidChallengeClaimIndexes.length; i++) {
uint256 proverBond = (CHALLENGER_BOND * PROVER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
_distributeBond(validityProofProvers[invalidChallengeClaimIndexes[i]], proverBond);
currentContractBalance = currentContractBalance - proverBond;
}
// refund the bond to proposer
_distributeBond(gameCreator(), PROPOSER_BOND);
currentContractBalance = currentContractBalance - PROPOSER_BOND;
} else {
// sanity check
revert InvalidGameStatus();
}
// transfer the rest
_distributeBond(FEE_VAULT_ADDRESS, currentContractBalance);

resolvedAt = Timestamp.wrap(uint64(block.timestamp));

// Update the status and emit the resolved event, note that we're performing an assignment here.
emit Resolved(status = status_);

// Try to update the anchor state, this should not revert.
ANCHOR_STATE_REGISTRY.tryUpdateAnchorState();
}
Comment on lines 383 to 471
function resolve() external returns (GameStatus status_) {
// INVARIANT: Resolution cannot occur unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
GameStatus parentStatus = parentGameStatus();
// parent game must be resolved
if (parentStatus == GameStatus.IN_PROGRESS) {
revert ParentGameNotResolved();
}
if (parentStatus == GameStatus.CHALLENGER_WINS) {
status_ = GameStatus.CHALLENGER_WINS;
// re-update fault proof prover
faultProofProver = payable(parentGameProxy().gameWinner());
}
if (isChallengeSuccess) {
status_ = GameStatus.CHALLENGER_WINS;
}

if (status_ != GameStatus.CHALLENGER_WINS) {
// first check if the challenge window is expired
if (block.timestamp - createdAt.raw() <= MAX_CLOCK_DURATION.raw()) {
revert ClockNotExpired();
}
// then check if there is any remaining challenges
for (uint256 i = 0; i < challengedClaimIndexes.length; i++) {
if (!invalidChallengeClaims[challengedClaimIndexes[i]]) {
revert UnresolvedChallenges();
}
}
status_ = GameStatus.DEFENDER_WINS;
}

uint256 currentContractBalance = WETH.balanceOf(address(this));
if (status_ == GameStatus.CHALLENGER_WINS) {
// refund valid challengers if there is any
for (uint256 i = 0; i < challengedClaimIndexes.length; i++) {
if (!invalidChallengeClaims[challengedClaimIndexes[i]]) {
// refund the bond
_distributeBond(challengers[challengedClaimIndexes[i]], CHALLENGER_BOND);
currentContractBalance = currentContractBalance - CHALLENGER_BOND;
}
}
// TODO reward part of challengers bond to validity provers, current reward is zero
uint256 initialBalance = currentContractBalance;
// reward the special challenger who submitted the signal which is proven to be valid
// 1. someone submitted a valid fault proof corresponding to the challenge index; or
// 2. the generate proof window is expired and no one submitted a validity proof
// If isChallengeSuccess is true, then the challenger exists;
// If isChallengeSuccess is false, then it indicates that the parent game is CHALLENGER_WINS and
// there is no successful challenge in the current game.
if (isChallengeSuccess) {
// there is a challenger who submmitted the dispute claim index by `challengeBySignal`
uint256 challengerBond = (currentContractBalance * CHALLENGER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
if (challengedClaims[successfulChallengeIndex]) {
_distributeBond(challengers[successfulChallengeIndex], challengerBond);
} else {
// if there is no challenger, then the challenger is the fault proof prover self
_distributeBond(faultProofProver, challengerBond);
}
currentContractBalance = currentContractBalance - challengerBond;
}
// reward the fault proof prover
uint256 proverBond = (initialBalance * PROVER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
_distributeBond(faultProofProver, proverBond);
currentContractBalance = currentContractBalance - proverBond;
} else if (status_ == GameStatus.DEFENDER_WINS) {
// reward part of challengers bond to validity provers
for (uint256 i = 0; i < invalidChallengeClaimIndexes.length; i++) {
uint256 proverBond = (CHALLENGER_BOND * PROVER_REWARD_PERCENTAGE) / PERCENTAGE_DIVISOR;
_distributeBond(validityProofProvers[invalidChallengeClaimIndexes[i]], proverBond);
currentContractBalance = currentContractBalance - proverBond;
}
// refund the bond to proposer
_distributeBond(gameCreator(), PROPOSER_BOND);
currentContractBalance = currentContractBalance - PROPOSER_BOND;
} else {
// sanity check
revert InvalidGameStatus();
}
// transfer the rest
_distributeBond(FEE_VAULT_ADDRESS, currentContractBalance);

resolvedAt = Timestamp.wrap(uint64(block.timestamp));

// Update the status and emit the resolved event, note that we're performing an assignment here.
emit Resolved(status = status_);

// Try to update the anchor state, this should not revert.
ANCHOR_STATE_REGISTRY.tryUpdateAnchorState();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants