diff --git a/contracts/CrossChainProofOfHumanity.sol b/contracts/CrossChainProofOfHumanity.sol index 0d344b2..5a43572 100644 --- a/contracts/CrossChainProofOfHumanity.sol +++ b/contracts/CrossChainProofOfHumanity.sol @@ -390,7 +390,7 @@ contract CrossChainProofOfHumanity is ICrossChainProofOfHumanity { return !humanity.isHomeChain && humanityId != bytes20(0x0) && humanity.owner == _account - && humanity.expirationTime > block.timestamp; + && humanity.expirationTime >= block.timestamp; } /** @notice Get the owner of a humanity. Returns null address if not claimed diff --git a/contracts/ProofOfHumanity.sol b/contracts/ProofOfHumanity.sol index 973d577..6c592f0 100644 --- a/contracts/ProofOfHumanity.sol +++ b/contracts/ProofOfHumanity.sol @@ -186,6 +186,8 @@ contract ProofOfHumanity is IProofOfHumanity, IArbitrable, IEvidence { uint256 public winnerStakeMultiplier; /// @dev Multiplier for calculating the fee stake paid by the party that lost the previous round. uint256 public loserStakeMultiplier; + /// @dev The number of humanities registered at some moment. + uint256 public humanityCount; /// @dev Gap for possible future versions storage layout changes. uint256[50] internal __gap; @@ -799,7 +801,10 @@ contract ProofOfHumanity is IProofOfHumanity, IArbitrable, IEvidence { requestBaseDeposit ); - require(_contribute(_humanityId, requestId, 0, 0, Party.Requester, totalCost)); + _contribute(_humanityId, requestId, 0, 0, Party.Requester, totalCost); + Round storage round = request.challenges[0].rounds[0]; + require(round.paidFees.forRequester >= totalCost); + round.sideFunded = Party.Requester; emit RevocationRequest(msg.sender, _humanityId, requestId); emit Evidence( @@ -828,6 +833,10 @@ contract ProofOfHumanity is IProofOfHumanity, IArbitrable, IEvidence { ); _contribute(_humanityId, _requestId, 0, 0, Party.Requester, totalCost); + Round storage round = request.challenges[0].rounds[0]; + + if (round.paidFees.forRequester >= totalCost) + round.sideFunded = Party.Requester; } /** @notice Vouch that the human corresponds to the humanity ID. @@ -1032,12 +1041,14 @@ contract ProofOfHumanity is IProofOfHumanity, IArbitrable, IEvidence { uint256 challengeId = request.lastChallengeId++; Challenge storage challenge = request.challenges[challengeId]; - Round storage round = challenge.rounds[0]; ArbitratorData memory arbitratorData = arbitratorDataHistory[request.arbitratorDataId]; uint256 arbitrationCost = arbitratorData.arbitrator.arbitrationCost(arbitratorData.arbitratorExtraData); - require(_contribute(_humanityId, _requestId, challengeId, 0, Party.Challenger, arbitrationCost)); + _contribute(_humanityId, _requestId, challengeId, 0, Party.Challenger, arbitrationCost); + Round storage round = challenge.rounds[0]; + require(round.paidFees.forChallenger >= arbitrationCost); + round.sideFunded = Party.None; // Set this back to 0, since it's no longer relevant as the new round is created. // Subtract the costs from the total of staked contributions. round.feeRewards = round.feeRewards.subCap(arbitrationCost); @@ -1108,37 +1119,32 @@ contract ProofOfHumanity is IProofOfHumanity, IArbitrable, IEvidence { Challenge storage challenge = request.challenges[disputeData.challengeId]; Round storage round = challenge.rounds[challenge.lastRoundId]; - Party firstFunded = round.sideFunded; - require(_side != firstFunded); + require(_side != round.sideFunded); uint256 appealCost = IArbitrator(_arbitrator).appealCost( _disputeId, arbitratorDataHistory[request.arbitratorDataId].arbitratorExtraData ); uint256 totalCost = appealCost.addCap(appealCost.mulCap(multiplier) / MULTIPLIER_DIVISOR); - - if ( - _contribute( - disputeData.humanityId, - disputeData.requestId, - disputeData.challengeId, - challenge.lastRoundId, - _side, - totalCost - ) && + _contribute(disputeData.humanityId, disputeData.requestId, disputeData.challengeId, challenge.lastRoundId, _side, totalCost); + uint256 paidFees = _side == Party.Requester ? round.paidFees.forRequester : round.paidFees.forChallenger; + if (paidFees >= totalCost) { // If firstFunded was assigned, it means other side was funded and if this one gets fully funded as well appeal can be created. - firstFunded != Party.None - ) { - IArbitrator(_arbitrator).appeal{value: appealCost}( - _disputeId, - arbitratorDataHistory[request.arbitratorDataId].arbitratorExtraData - ); - challenge.lastRoundId++; - - // Subtract the costs from the total of staked contributions - round.feeRewards = round.feeRewards.subCap(appealCost); - - emit AppealCreated(IArbitrator(_arbitrator), _disputeId); + if (round.sideFunded == Party.None) { + round.sideFunded = _side; + } else { + IArbitrator(_arbitrator).appeal{value: appealCost}( + _disputeId, + arbitratorDataHistory[request.arbitratorDataId].arbitratorExtraData + ); + challenge.lastRoundId++; + + // Subtract the costs from the total of staked contributions + round.feeRewards = round.feeRewards.subCap(appealCost); + round.sideFunded = Party.None; // Set this back to default in the past round as it's no longer relevant. + + emit AppealCreated(IArbitrator(_arbitrator), _disputeId); + } } } @@ -1410,6 +1416,7 @@ contract ProofOfHumanity is IProofOfHumanity, IArbitrable, IEvidence { Humanity storage humanity = humanityData[_humanityId]; requestId = humanity.requests.length; + if (requestId == 0) humanityCount++; Request storage request = humanity.requests.push(); request.requester = payable(msg.sender); @@ -1425,7 +1432,11 @@ contract ProofOfHumanity is IProofOfHumanity, IArbitrable, IEvidence { uint256 totalCost = arbitratorData.arbitrator.arbitrationCost(arbitratorData.arbitratorExtraData).addCap( requestBaseDeposit ); + _contribute(_humanityId, requestId, 0, 0, Party.Requester, totalCost); + Round storage round = request.challenges[0].rounds[0]; + if (round.paidFees.forRequester >= totalCost) + round.sideFunded = Party.Requester; } /** @dev Make a fee contribution. Reimburse remaining ETH. @@ -1435,7 +1446,6 @@ contract ProofOfHumanity is IProofOfHumanity, IArbitrable, IEvidence { * @param _roundId Round to contribute to. * @param _side Side to contribute to. * @param _totalRequired Total amount required for this side. - * @return paidInFull Whether the contribution was paid in full. */ function _contribute( bytes20 _humanityId, @@ -1444,9 +1454,9 @@ contract ProofOfHumanity is IProofOfHumanity, IArbitrable, IEvidence { uint256 _roundId, Party _side, uint256 _totalRequired - ) internal returns (bool paidInFull) { + ) internal { Round storage round = humanityData[_humanityId].requests[_requestId].challenges[_challengeId].rounds[_roundId]; - + uint256 remainingETH; uint256 contribution = msg.value; uint256 requiredAmount = _totalRequired.subCap( @@ -1455,9 +1465,6 @@ contract ProofOfHumanity is IProofOfHumanity, IArbitrable, IEvidence { if (requiredAmount <= msg.value) { contribution = requiredAmount; remainingETH = msg.value - requiredAmount; - - paidInFull = true; - round.sideFunded = round.sideFunded == Party.None ? _side : Party.None; } if (_side == Party.Requester) { @@ -1672,4 +1679,10 @@ contract ProofOfHumanity is IProofOfHumanity, IArbitrable, IEvidence { function getNumberOfVouches(bytes20 _humanityId, uint256 _requestId) external view returns (uint256) { return humanityData[_humanityId].requests[_requestId].vouches.length; } + + /** @notice Get the number of humanities registered at some moment. + */ + function getHumanityCount() external view returns (uint256) { + return humanityCount; + } } diff --git a/contracts/ProofOfHumanityProxyV2.sol b/contracts/ProofOfHumanityProxyV2.sol new file mode 100644 index 0000000..453a723 --- /dev/null +++ b/contracts/ProofOfHumanityProxyV2.sol @@ -0,0 +1,102 @@ +/** + * @authors: [@unknownunknown1*, @clesaege] + * @reviewers: [] + * @auditors: [] + * @bounties: [] + * @deployments: [] + * @tools: [] + */ + +pragma solidity 0.8.20; + +import {IProofOfHumanity} from "./interfaces/IProofOfHumanity.sol"; + +/** + * @title ProofOfHumanityProxyV2 + * A proxy contract for ProofOfHumanity that implements a token interface to interact with other dapps. + * Note that it isn't an ERC20 and only implements its interface in order to be compatible with Snapshot. + */ +contract ProofOfHumanityProxyV2 { + + // ========== STORAGE ========== + + /// @dev The address that can make governance changes to the parameters of the contract. + address public governor; + + /// @dev Instance of the ProofOfHumanity contract + IProofOfHumanity public proofOfHumanity; + + string public name = "Human Vote"; + string public symbol = "VOTE"; + uint8 public decimals = 0; + + /* Modifiers */ + + modifier onlyGovernor() { + require(msg.sender == governor); + _; + } + + // ========== CONSTRUCTOR ========== + + /** @dev Constructor. + * @param _proofOfHumanity The address of the related ProofOfHumanity contract. + */ + constructor(IProofOfHumanity _proofOfHumanity) { + proofOfHumanity = _proofOfHumanity; + } + + + /** @dev Changes the address of the the related ProofOfHumanity contract. + * @param _proofOfHumanity The address of the new contract. + */ + function changePoH(IProofOfHumanity _proofOfHumanity) external onlyGovernor { + proofOfHumanity = _proofOfHumanity; + } + + /** @dev Changes the address of the the governor. + * @param _governor The address of the new governor. + */ + function changeGovernor(address _governor) external onlyGovernor { + //require(msg.sender == governor, "The caller must be the governor."); + governor = _governor; + } + + + /** @dev Returns true if the account corresponds to a claimed humanity. + * @param _account The account address. + * @return Whether the account is registered or not. + */ + function isHuman(address _account) public view returns (bool) { + return proofOfHumanity.isHuman(_account); + } + + // ******************** // + // * IERC20 * // + // ******************** // + + /** @dev Returns the balance of a particular account of the ProofOfHumanity contract. + * Note that this function takes the expiration date into account. + * @param _account The account address. + * @return The balance of the account. + */ + function balanceOf(address _account) external view returns (uint256) { + return isHuman(_account) ? 1 : 0; + } + + /** @dev Returns the count of all humanities that made a registration request at some point. + * Note that with the current implementation of ProofOfHumanity it'd be very costly to count only the humanities that are currently registered. + * @return The total count of humanities. + */ + function totalSupply() external view returns (uint256) { + return proofOfHumanity.getHumanityCount(); + } + + function transfer(address _recipient, uint256 _amount) external pure returns (bool) { return false; } + + function allowance(address _owner, address _spender) external view returns (uint256) {} + + function approve(address _spender, uint256 _amount) external pure returns (bool) { return false; } + + function transferFrom(address _sender, address _recipient, uint256 _amount) external pure returns (bool) { return false; } +} \ No newline at end of file diff --git a/contracts/extending-old/ProofOfHumanityExtended.sol b/contracts/extending-old/ProofOfHumanityExtended.sol index 33aec51..4ca9e42 100644 --- a/contracts/extending-old/ProofOfHumanityExtended.sol +++ b/contracts/extending-old/ProofOfHumanityExtended.sol @@ -185,6 +185,8 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { uint256 public winnerStakeMultiplier; /// @dev Multiplier for calculating the fee stake paid by the party that lost the previous round. uint256 public loserStakeMultiplier; + /// @dev The number of humanities registered at some moment. Doesn't include profiles from V1. + uint256 public humanityCount; /// @dev Fork Module instance to be used for interacting with v1 state. IForkModule private forkModule; @@ -544,20 +546,17 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { function ccDischargeHumanity( address _account ) external onlyCrossChain returns (bytes20 humanityId, uint40 expirationTime) { - humanityId = accountHumanity[_account]; + humanityId = humanityOf(_account); Humanity storage humanity = humanityData[humanityId]; require(humanity.nbPendingRequests == 0); + require(!humanity.vouching); - if (humanity.owner == _account && block.timestamp < humanity.expirationTime) { - require(!humanity.vouching); + if (humanity.owner == _account && block.timestamp < humanity.expirationTime) { expirationTime = humanity.expirationTime; delete humanity.owner; } else { - // V1 profiles have default humanity. - humanityId = bytes20(_account); - // Should revert in case account is not registered. expirationTime = forkModule.tryRemove(_account); } @@ -828,7 +827,10 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { requestBaseDeposit ); - require(_contribute(_humanityId, requestId, 0, 0, Party.Requester, totalCost)); + _contribute(_humanityId, requestId, 0, 0, Party.Requester, totalCost); + Round storage round = request.challenges[0].rounds[0]; + require(round.paidFees.forRequester >= totalCost); + round.sideFunded = Party.Requester; emit RevocationRequest(msg.sender, _humanityId, requestId); emit Evidence( @@ -857,6 +859,10 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { ); _contribute(_humanityId, _requestId, 0, 0, Party.Requester, totalCost); + Round storage round = request.challenges[0].rounds[0]; + + if (round.paidFees.forRequester >= totalCost) + round.sideFunded = Party.Requester; } /** @notice Vouch that the human corresponds to the humanity id. @@ -1061,12 +1067,14 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { uint256 challengeId = request.lastChallengeId++; Challenge storage challenge = request.challenges[challengeId]; - Round storage round = challenge.rounds[0]; ArbitratorData memory arbitratorData = arbitratorDataHistory[request.arbitratorDataId]; uint256 arbitrationCost = arbitratorData.arbitrator.arbitrationCost(arbitratorData.arbitratorExtraData); - require(_contribute(_humanityId, _requestId, challengeId, 0, Party.Challenger, arbitrationCost)); + _contribute(_humanityId, _requestId, challengeId, 0, Party.Challenger, arbitrationCost); + Round storage round = challenge.rounds[0]; + require(round.paidFees.forChallenger >= arbitrationCost); + round.sideFunded = Party.None; // Set this back to 0, since it's no longer relevant as the new round is created. // Subtract the costs from the total of staked contributions. round.feeRewards = round.feeRewards.subCap(arbitrationCost); @@ -1137,8 +1145,7 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { Challenge storage challenge = request.challenges[disputeData.challengeId]; Round storage round = challenge.rounds[challenge.lastRoundId]; - Party firstFunded = round.sideFunded; - require(_side != firstFunded); + require(_side != round.sideFunded); uint256 appealCost = IArbitrator(_arbitrator).appealCost( _disputeId, @@ -1146,28 +1153,25 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { ); uint256 totalCost = appealCost.addCap(appealCost.mulCap(multiplier) / MULTIPLIER_DIVISOR); - if ( - _contribute( - disputeData.humanityId, - disputeData.requestId, - disputeData.challengeId, - challenge.lastRoundId, - _side, - totalCost - ) && + _contribute(disputeData.humanityId, disputeData.requestId, disputeData.challengeId, challenge.lastRoundId, _side, totalCost); + uint256 paidFees = _side == Party.Requester ? round.paidFees.forRequester : round.paidFees.forChallenger; + if (paidFees >= totalCost) { // If firstFunded was assigned, it means other side was funded and if this one gets fully funded as well appeal can be created. - firstFunded != Party.None - ) { - IArbitrator(_arbitrator).appeal{value: appealCost}( - _disputeId, - arbitratorDataHistory[request.arbitratorDataId].arbitratorExtraData - ); - challenge.lastRoundId++; - - // Subtract the costs from the total of staked contributions - round.feeRewards = round.feeRewards.subCap(appealCost); - - emit AppealCreated(IArbitrator(_arbitrator), _disputeId); + if (round.sideFunded == Party.None) { + round.sideFunded = _side; + } else { + IArbitrator(_arbitrator).appeal{value: appealCost}( + _disputeId, + arbitratorDataHistory[request.arbitratorDataId].arbitratorExtraData + ); + challenge.lastRoundId++; + + // Subtract the costs from the total of staked contributions + round.feeRewards = round.feeRewards.subCap(appealCost); + round.sideFunded = Party.None; // Set this back to default in the past round as it's no longer relevant. + + emit AppealCreated(IArbitrator(_arbitrator), _disputeId); + } } } @@ -1190,10 +1194,9 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { require(request.challengePeriodStart + challengePeriodDuration < block.timestamp); if (request.revocation) { + humanity.pendingRevocation = false; if (humanity.owner != address(0x0) && block.timestamp < humanity.expirationTime) { - delete humanity.owner; - humanity.pendingRevocation = false; - + delete humanity.owner; // If not claimed in this contract, directly remove in fork module. } else forkModule.remove(address(_humanityId)); @@ -1201,6 +1204,8 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { } else if (!request.punishedVouch) { humanity.owner = request.requester; humanity.expirationTime = uint40(block.timestamp).addCap40(humanityLifespan); + // Register profile on V2 and remove it on V1 if it's present there. + if (forkModule.isRegistered(address(_humanityId))) forkModule.remove(address(_humanityId)); emit HumanityClaimed(_humanityId, _requestId); } @@ -1255,8 +1260,12 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { delete voucherHumanity.owner; - // If not claimed in this contract, directly remove in fork module. - } else forkModule.remove(address(voucherHumanityId)); + // If not claimed in this contract, directly remove in fork module. + } else { + uint256 voucherRequestCount = voucherHumanity.requestCount[address(voucherHumanityId)]; + if (voucherRequestCount != 0) voucherHumanity.requests[voucherRequestCount - 1].punishedVouch = true; + forkModule.remove(address(voucherHumanityId)); + } emit HumanityDischargedDirectly(voucherHumanityId); } @@ -1391,6 +1400,9 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { if (request.usedReasons == FULL_REASONS_SET) { humanity.owner = request.requester; humanity.expirationTime = uint40(block.timestamp).addCap40(humanityLifespan); + // Register profile on V2 and remove it on V1 if it's present there. + if (forkModule.isRegistered(address(disputeData.humanityId))) + forkModule.remove(address(disputeData.humanityId)); emit HumanityClaimed(disputeData.humanityId, disputeData.requestId); } else { @@ -1451,6 +1463,7 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { Humanity storage humanity = humanityData[_humanityId]; requestId = humanity.requests.length; + if (requestId == 0) humanityCount++; Request storage request = humanity.requests.push(); request.requester = payable(msg.sender); @@ -1466,7 +1479,11 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { uint256 totalCost = arbitratorData.arbitrator.arbitrationCost(arbitratorData.arbitratorExtraData).addCap( requestBaseDeposit ); + _contribute(_humanityId, requestId, 0, 0, Party.Requester, totalCost); + Round storage round = request.challenges[0].rounds[0]; + if (round.paidFees.forRequester >= totalCost) + round.sideFunded = Party.Requester; } /** @dev Make a fee contribution. Reimburse remaining ETH. @@ -1476,7 +1493,6 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { * @param _roundId Round to contribute to. * @param _side Side to contribute to. * @param _totalRequired Total amount required for this side. - * @return paidInFull Whether the contribution was paid in full. */ function _contribute( bytes20 _humanityId, @@ -1485,9 +1501,9 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { uint256 _roundId, Party _side, uint256 _totalRequired - ) internal returns (bool paidInFull) { + ) internal { Round storage round = humanityData[_humanityId].requests[_requestId].challenges[_challengeId].rounds[_roundId]; - + uint256 remainingETH; uint256 contribution = msg.value; uint256 requiredAmount = _totalRequired.subCap( @@ -1496,9 +1512,6 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { if (requiredAmount <= msg.value) { contribution = requiredAmount; remainingETH = msg.value - requiredAmount; - - paidInFull = true; - round.sideFunded = round.sideFunded == Party.None ? _side : Party.None; } if (_side == Party.Requester) { @@ -1724,4 +1737,10 @@ contract ProofOfHumanityExtended is IProofOfHumanity, IArbitrable, IEvidence { function getNumberOfVouches(bytes20 _humanityId, uint256 _requestId) external view returns (uint256) { return humanityData[_humanityId].requests[_requestId].vouches.length; } + + /** @notice Get the number of humanities registered at some moment. Note that profiles from V1 aren't included. + */ + function getHumanityCount() external view returns (uint256) { + return humanityCount; + } } diff --git a/contracts/interfaces/IProofOfHumanity.sol b/contracts/interfaces/IProofOfHumanity.sol index 8031003..deb2d56 100644 --- a/contracts/interfaces/IProofOfHumanity.sol +++ b/contracts/interfaces/IProofOfHumanity.sol @@ -35,4 +35,6 @@ interface IProofOfHumanity { address owner, uint256 nbRequests ); + + function getHumanityCount() external view returns (uint256); }