[entropy] audit: 3. two step transfer (#1178)

* two step owner transfer

* admin two step transfer

* remove redundant test
This commit is contained in:
Dev Kalra 2023-12-13 16:45:07 +05:30 committed by GitHub
parent 941ee777f0
commit f0c67c5996
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 108 additions and 33 deletions

View File

@ -9,31 +9,49 @@ import "./EntropyState.sol";
* @dev `Governance` defines a means to enacting changes to the Entropy contract. * @dev `Governance` defines a means to enacting changes to the Entropy contract.
*/ */
abstract contract EntropyGovernance is EntropyState { abstract contract EntropyGovernance is EntropyState {
event AdminSet(address oldAdmin, address newAdmin);
event PythFeeSet(uint oldPythFee, uint newPythFee); event PythFeeSet(uint oldPythFee, uint newPythFee);
event DefaultProviderSet( event DefaultProviderSet(
address oldDefaultProvider, address oldDefaultProvider,
address newDefaultProvider address newDefaultProvider
); );
function getAdmin() external view returns (address) { event NewAdminProposed(address oldAdmin, address newAdmin);
return _state.admin; event NewAdminAccepted(address oldAdmin, address newAdmin);
/**
* @dev Returns the address of the proposed admin.
*/
function proposedAdmin() public view virtual returns (address) {
return _state.proposedAdmin;
} }
/** /**
* @dev Set the admin of the contract. * @dev Proposes a new admin of the contract. Replaces the proposed admin if there is one.
* * Can only be called by either admin or owner.
* Calls {_authoriseAdminAction}.
*
* Emits an {AdminSet} event.
*/ */
function setAdmin(address newAdmin) external { function proposeAdmin(address newAdmin) public virtual {
_authoriseAdminAction(); _authoriseAdminAction();
address oldAdmin = _state.admin; _state.proposedAdmin = newAdmin;
_state.admin = newAdmin; emit NewAdminProposed(_state.admin, newAdmin);
}
emit AdminSet(oldAdmin, newAdmin); /**
* @dev The proposed admin accepts the admin transfer.
*/
function acceptAdmin() external {
if (msg.sender != _state.proposedAdmin)
revert EntropyErrors.Unauthorized();
address oldAdmin = _state.admin;
_state.admin = msg.sender;
_state.proposedAdmin = address(0);
emit NewAdminAccepted(oldAdmin, msg.sender);
}
function getAdmin() external view returns (address) {
return _state.admin;
} }
/** /**

View File

@ -34,6 +34,9 @@ contract EntropyInternalStructs {
mapping(bytes32 => EntropyStructs.Request) requestsOverflow; mapping(bytes32 => EntropyStructs.Request) requestsOverflow;
// Mapping from randomness providers to information about each them. // Mapping from randomness providers to information about each them.
mapping(address => EntropyStructs.ProviderInfo) providers; mapping(address => EntropyStructs.ProviderInfo) providers;
// proposedAdmin is the new admin's account address proposed by either the owner or the current admin.
// If there is no pending transfer request, this value will hold `address(0)`.
address proposedAdmin;
} }
} }

View File

@ -3,7 +3,7 @@ pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import "@pythnetwork/entropy-sdk-solidity/EntropyErrors.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyErrors.sol";
import "./EntropyGovernance.sol"; import "./EntropyGovernance.sol";
@ -11,7 +11,7 @@ import "./Entropy.sol";
contract EntropyUpgradable is contract EntropyUpgradable is
Initializable, Initializable,
OwnableUpgradeable, Ownable2StepUpgradeable,
UUPSUpgradeable, UUPSUpgradeable,
Entropy, Entropy,
EntropyGovernance EntropyGovernance
@ -42,7 +42,7 @@ contract EntropyUpgradable is
); );
// We need to transfer the ownership from deployer to the new owner // We need to transfer the ownership from deployer to the new owner
transferOwnership(owner); _transferOwnership(owner);
} }
/// Ensures the contract cannot be uninitialized and taken over. /// Ensures the contract cannot be uninitialized and taken over.

View File

@ -24,6 +24,8 @@ contract EntropyAuthorized is Test, EntropyTestUtils {
address public provider1 = address(4); address public provider1 = address(4);
address public provider2 = address(5); address public provider2 = address(5);
address public owner2 = address(6);
uint128 pythFeeInWei = 7; uint128 pythFeeInWei = 7;
function setUp() public { function setUp() public {
@ -39,24 +41,6 @@ contract EntropyAuthorized is Test, EntropyTestUtils {
random.initialize(owner, admin, pythFeeInWei, provider1, false); random.initialize(owner, admin, pythFeeInWei, provider1, false);
} }
function testSetAdminByAdmin() public {
vm.prank(admin);
random.setAdmin(admin2);
assertEq(random.getAdmin(), admin2);
}
function testSetAdminByOwner() public {
vm.prank(owner);
random.setAdmin(admin2);
assertEq(random.getAdmin(), admin2);
}
function testExpectRevertSetAdminByUnauthorized() public {
vm.expectRevert(EntropyErrors.Unauthorized.selector);
vm.prank(admin2);
random.setAdmin(admin);
}
function testSetPythFeeByAdmin() public { function testSetPythFeeByAdmin() public {
vm.prank(admin); vm.prank(admin);
random.setPythFee(10); random.setPythFee(10);
@ -120,4 +104,74 @@ contract EntropyAuthorized is Test, EntropyTestUtils {
vm.prank(owner); vm.prank(owner);
random.upgradeTo(address(randomDifferentMagic)); random.upgradeTo(address(randomDifferentMagic));
} }
function testExpectRevertRequestOwnershipTransferByUnauthorized() public {
vm.expectRevert("Ownable: caller is not the owner");
vm.prank(provider1);
random.transferOwnership(owner2);
}
function testExpectRevertRequestOwnershipTransferByAdmin() public {
vm.expectRevert("Ownable: caller is not the owner");
vm.prank(admin);
random.transferOwnership(owner2);
}
function testRequestAndAcceptOwnershipTransfer() public {
vm.prank(owner);
random.transferOwnership(owner2);
assertEq(random.pendingOwner(), owner2);
vm.prank(owner2);
random.acceptOwnership();
assertEq(random.owner(), owner2);
}
function testRequestAndAcceptOwnershipTransferUnauthorizedAccept() public {
vm.prank(owner);
random.transferOwnership(owner2);
assertEq(random.pendingOwner(), owner2);
vm.prank(admin);
vm.expectRevert("Ownable2Step: caller is not the new owner");
random.acceptOwnership();
}
function testProposeAdminByOwner() public {
vm.prank(owner);
random.proposeAdmin(admin2);
assertEq(random.proposedAdmin(), admin2);
}
function testProposeAdminByAdmin() public {
vm.prank(admin);
random.proposeAdmin(admin2);
assertEq(random.proposedAdmin(), admin2);
}
function testProposeAdminByUnauthorized() public {
vm.expectRevert(EntropyErrors.Unauthorized.selector);
random.proposeAdmin(admin2);
}
function testAcceptAdminByPropsed() public {
vm.prank(owner);
random.proposeAdmin(admin2);
vm.prank(admin2);
random.acceptAdmin();
assertEq(random.getAdmin(), admin2);
}
function testAcceptAdminByUnauthorized() public {
vm.prank(owner);
random.proposeAdmin(admin2);
vm.prank(provider1);
vm.expectRevert(EntropyErrors.Unauthorized.selector);
random.acceptAdmin();
}
} }