-
Notifications
You must be signed in to change notification settings - Fork 172
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
base: develop
Are you sure you want to change the base?
Conversation
prepare for v0.5.1
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)); | ||
} | ||
} |
Check warning
Code scanning / Slither
Unused return Medium
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(); | ||
} |
Check warning
Code scanning / Slither
Reentrancy vulnerabilities Medium
External calls:
- _distributeBond(validityProofProvers[invalidChallengeClaimIndexes[i_scope_1]],proverBond_scope_2)
- WETH.unlock(_recipient,_bond)
- _distributeBond(gameCreator(),PROPOSER_BOND)
- WETH.unlock(_recipient,_bond)
State variables written after the call(s):
- _distributeBond(gameCreator(),PROPOSER_BOND)
- credit[_recipient] += _bond
ZkFaultDisputeGame.credit can be used in cross function reentrancies:
- ZkFaultDisputeGame._distributeBond(address,uint256)
- ZkFaultDisputeGame.claimCredit(address)
- ZkFaultDisputeGame.credit
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
External calls:
- _distributeBond(challengers[challengedClaimIndexes[i_scope_0]],CHALLENGER_BOND)
- WETH.unlock(_recipient,_bond)
- _distributeBond(challengers[successfulChallengeIndex],challengerBond)
- WETH.unlock(_recipient,_bond)
- _distributeBond(faultProofProver,challengerBond)
- WETH.unlock(_recipient,_bond)
- _distributeBond(faultProofProver,proverBond)
- WETH.unlock(_recipient,_bond)
- _distributeBond(validityProofProvers[invalidChallengeClaimIndexes[i_scope_1]],proverBond_scope_2)
- WETH.unlock(_recipient,_bond)
- _distributeBond(gameCreator(),PROPOSER_BOND)
- WETH.unlock(_recipient,_bond)
- _distributeBond(FEE_VAULT_ADDRESS,currentContractBalance)
- WETH.unlock(_recipient,_bond)
State variables written after the call(s):
- _distributeBond(FEE_VAULT_ADDRESS,currentContractBalance)
- credit[_recipient] += _bond
ZkFaultDisputeGame.credit can be used in cross function reentrancies:
- ZkFaultDisputeGame._distributeBond(address,uint256)
- ZkFaultDisputeGame.claimCredit(address)
- ZkFaultDisputeGame.credit
- Resolved(status = status_)
ZkFaultDisputeGame.status can be used in cross function reentrancies:
- ZkFaultDisputeGame.challengeByProof(uint256,Claim,Claim[],bytes)
- ZkFaultDisputeGame.challengeBySignal(uint256)
- ZkFaultDisputeGame.gameWinner()
- ZkFaultDisputeGame.resolve()
- ZkFaultDisputeGame.resolveClaim()
- ZkFaultDisputeGame.status
- ZkFaultDisputeGame.submitProofForSignal(uint256,Claim[],bytes)
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
External calls:
- _distributeBond(challengers[challengedClaimIndexes[i_scope_0]],CHALLENGER_BOND)
- WETH.unlock(_recipient,_bond)
- _distributeBond(challengers[successfulChallengeIndex],challengerBond)
- WETH.unlock(_recipient,_bond)
State variables written after the call(s):
- _distributeBond(challengers[successfulChallengeIndex],challengerBond)
- credit[_recipient] += _bond
ZkFaultDisputeGame.credit can be used in cross function reentrancies:
- ZkFaultDisputeGame._distributeBond(address,uint256)
- ZkFaultDisputeGame.claimCredit(address)
- ZkFaultDisputeGame.credit
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
External calls:
- _distributeBond(validityProofProvers[invalidChallengeClaimIndexes[i_scope_1]],proverBond_scope_2)
- WETH.unlock(_recipient,_bond)
- _distributeBond(gameCreator(),PROPOSER_BOND)
- WETH.unlock(_recipient,_bond)
State variables written after the call(s):
- _distributeBond(gameCreator(),PROPOSER_BOND)
- credit[_recipient] += _bond
ZkFaultDisputeGame.credit can be used in cross function reentrancies:
- ZkFaultDisputeGame._distributeBond(address,uint256)
- ZkFaultDisputeGame.claimCredit(address)
- ZkFaultDisputeGame.credit
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
External calls:
- _distributeBond(challengers[challengedClaimIndexes[i_scope_0]],CHALLENGER_BOND)
- WETH.unlock(_recipient,_bond)
- _distributeBond(challengers[successfulChallengeIndex],challengerBond)
- WETH.unlock(_recipient,_bond)
- _distributeBond(faultProofProver,challengerBond)
- WETH.unlock(_recipient,_bond)
- _distributeBond(faultProofProver,proverBond)
- WETH.unlock(_recipient,_bond)
State variables written after the call(s):
- _distributeBond(faultProofProver,proverBond)
- credit[_recipient] += _bond
ZkFaultDisputeGame.credit can be used in cross function reentrancies:
- ZkFaultDisputeGame._distributeBond(address,uint256)
- ZkFaultDisputeGame.claimCredit(address)
- ZkFaultDisputeGame.credit
# Conflicts: # packages/contracts-bedrock/test/dispute/ZkFaultDisputeGame.t.sol
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: