Skip to content

Commit

Permalink
refactor(protocol): merge Guardians.sol into GuardianProver.sol (taik…
Browse files Browse the repository at this point in the history
…oxyz#17285)

Co-authored-by: dantaik <[email protected]>
  • Loading branch information
dantaik and dantaik authored May 21, 2024
1 parent 140aa25 commit dc313ff
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 165 deletions.
1 change: 0 additions & 1 deletion packages/protocol/contract_layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@
| version | uint32 | 254 | 0 | 4 | contracts/L1/provers/GuardianProver.sol:GuardianProver |
| minGuardians | uint32 | 254 | 4 | 4 | contracts/L1/provers/GuardianProver.sol:GuardianProver |
| __gap | uint256[46] | 255 | 0 | 1472 | contracts/L1/provers/GuardianProver.sol:GuardianProver |
| __gap | uint256[50] | 301 | 0 | 1600 | contracts/L1/provers/GuardianProver.sol:GuardianProver |

## TaikoToken
| Name | Type | Slot | Offset | Bytes | Contract |
Expand Down
141 changes: 132 additions & 9 deletions packages/protocol/contracts/L1/provers/GuardianProver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,37 @@ pragma solidity 0.8.24;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import "../../common/EssentialContract.sol";
import "../../common/LibStrings.sol";
import "../../verifiers/IVerifier.sol";
import "../tiers/ITierProvider.sol";
import "../ITaikoL1.sol";
import "./Guardians.sol";

/// @title GuardianProver
/// This prover uses itself as the verifier.
/// @custom:security-contact [email protected]
contract GuardianProver is IVerifier, Guardians {
contract GuardianProver is IVerifier, EssentialContract {
using SafeERC20 for IERC20;

error GV_PERMISSION_DENIED();
error GV_ZERO_ADDRESS();
/// @notice Contains the index of the guardian in `guardians` plus one (zero means not a
/// guardian)
/// @dev Slot 1
mapping(address guardian => uint256 id) public guardianIds;

/// @notice Mapping to store the approvals for a given hash, for a given version
mapping(uint32 version => mapping(bytes32 hash => uint256 approvalBits)) internal _approvals;

/// @notice The set of guardians
/// @dev Slot 3
address[] public guardians;

/// @notice The version of the guardians
/// @dev Slot 4
uint32 public version;

uint256[50] private __gap;
/// @notice The minimum number of guardians required to approve
uint32 public minGuardians;

uint256[46] private __gap;

/// @notice Emitted when a guardian proof is approved.
/// @param addr The address of the guardian.
Expand All @@ -35,13 +49,76 @@ contract GuardianProver is IVerifier, Guardians {
bytes proofData
);

/// @notice Emitted when the set of guardians is updated
/// @param version The new version
/// @param guardians The new set of guardians
event GuardiansUpdated(uint32 version, address[] guardians);

/// @notice Emitted when an approval is made
/// @param operationId The operation ID
/// @param approvalBits The new approval bits
/// @param minGuardiansReached If the proof was submitted
event Approved(uint256 indexed operationId, uint256 approvalBits, bool minGuardiansReached);

error GP_INVALID_GUARDIAN();
error GP_INVALID_GUARDIAN_SET();
error GP_INVALID_MIN_GUARDIANS();
error GV_PERMISSION_DENIED();
error GV_ZERO_ADDRESS();

/// @notice Initializes the contract.
/// @param _owner The owner of this contract. msg.sender will be used if this value is zero.
/// @param _addressManager The address of the {AddressManager} contract.
function init(address _owner, address _addressManager) external initializer {
__Essential_init(_owner, _addressManager);
}

/// @notice Set the set of guardians
/// @param _newGuardians The new set of guardians
/// @param _minGuardians The minimum required to sign
function setGuardians(
address[] memory _newGuardians,
uint8 _minGuardians
)
external
onlyOwner
nonReentrant
{
// We need at most 255 guardians (so the approval bits fit in a uint256)
if (_newGuardians.length == 0 || _newGuardians.length > type(uint8).max) {
revert GP_INVALID_GUARDIAN_SET();
}
// Minimum number of guardians to approve is at least equal or greater than half the
// guardians (rounded up) and less or equal than the total number of guardians
if (_minGuardians == 0 || _minGuardians > _newGuardians.length) {
revert GP_INVALID_MIN_GUARDIANS();
}

// Delete the current guardians
for (uint256 i; i < guardians.length; ++i) {
delete guardianIds[guardians[i]];
}
delete guardians;

// Set the new guardians
for (uint256 i; i < _newGuardians.length; ++i) {
address guardian = _newGuardians[i];
if (guardian == address(0)) revert GP_INVALID_GUARDIAN();
// This makes sure there are not duplicate addresses
if (guardianIds[guardian] != 0) revert GP_INVALID_GUARDIAN_SET();

// Save and index the guardian
guardians.push(guardian);
guardianIds[guardian] = guardians.length;
}

// Bump the version so previous approvals get invalidated
++version;

minGuardians = _minGuardians;
emit GuardiansUpdated(version, _newGuardians);
}

/// @notice Enables unlimited allowance for Taiko L1 contract.
/// param _enable true if unlimited allowance is approved, false to set the allowance to 0.
function enableTaikoTokenAllowance(bool _enable) external onlyOwner {
Expand Down Expand Up @@ -77,12 +154,12 @@ contract GuardianProver is IVerifier, Guardians {
returns (bool approved_)
{
bytes32 hash = keccak256(abi.encode(_meta, _tran, _proof.data));
approved_ = approve(_meta.id, hash);
approved_ = _approve(_meta.id, hash);

emit GuardianApproval(msg.sender, _meta.id, _tran.blockHash, approved_, _proof.data);

if (approved_) {
deleteApproval(hash);
_deleteApproval(hash);
ITaikoL1(resolve(LibStrings.B_TAIKO, false)).proveBlock(
_meta.id, abi.encode(_meta, _tran, _proof)
);
Expand All @@ -100,4 +177,50 @@ contract GuardianProver is IVerifier, Guardians {
{
if (_ctx.msgSender != address(this)) revert GV_PERMISSION_DENIED();
}

/// @notice Returns if the hash is approved
/// @param _hash The hash to check
/// @return true if the hash is approved
function isApproved(bytes32 _hash) public view returns (bool) {
return _isApproved(_approvals[version][_hash]);
}

/// @notice Returns the number of guardians
/// @return The number of guardians
function numGuardians() public view returns (uint256) {
return guardians.length;
}

function _approve(uint256 _blockId, bytes32 _hash) internal returns (bool approved_) {
uint256 id = guardianIds[msg.sender];
if (id == 0) revert GP_INVALID_GUARDIAN();

uint32 _version = version;

unchecked {
_approvals[_version][_hash] |= 1 << (id - 1);
}

uint256 _approval = _approvals[_version][_hash];
approved_ = _isApproved(_approval);
emit Approved(_blockId, _approval, approved_);
}

function _deleteApproval(bytes32 _hash) private {
delete _approvals[version][_hash];
}

function _isApproved(uint256 _approvalBits) private view returns (bool) {
uint256 count;
uint256 bits = _approvalBits;
uint256 guardiansLength = guardians.length;
unchecked {
for (uint256 i; i < guardiansLength; ++i) {
if (bits & 1 == 1) ++count;
if (count == minGuardians) return true;
bits >>= 1;
}
}
return false;
}
}
137 changes: 0 additions & 137 deletions packages/protocol/contracts/L1/provers/Guardians.sol

This file was deleted.

Loading

0 comments on commit dc313ff

Please sign in to comment.