Skip to content

Commit e6efc36

Browse files
authored
v0.6.4-testnet (#290)
2 parents e414ad3 + 1208da9 commit e6efc36

13 files changed

+1547
-134
lines changed

contracts/interfaces/IProfile.sol

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,23 @@ interface IProfile {
3030
event ProfileAdded(address indexed id);
3131
/// @dev Event emitted when a address in a profile is changed.
3232
event ProfileAddressChanged(address indexed id, RoleAccess indexed addressType);
33+
/// @dev Event emitted when the pubkey of the `id` is changed.
34+
event PubkeyChanged(address indexed id, bytes pubkey);
3335

3436
/// @dev Error of already existed profile.
3537
error ErrExistentProfile();
3638
/// @dev Error of non existed profile.
3739
error ErrNonExistentProfile();
40+
/// @dev Error when create a new profile whose id and consensus are not identical.
41+
error ErrIdAndConsensusDiffer();
42+
/**
43+
* @dev Error when there is a duplicated info of `value`, which is uin256-padding value of any address or hash of public key,
44+
* and with value type of `infoType`.
45+
*/
46+
error ErrDuplicatedInfo(RoleAccess infoType, uint256 value);
47+
error ErrDuplicatedPubkey(bytes pubkey);
48+
error ErrZeroAddress(RoleAccess infoType);
49+
error ErrZeroPubkey();
3850

3951
/// @dev Getter to query full `profile` from `id` address.
4052
function getId2Profile(address id) external view returns (CandidateProfile memory profile);
@@ -57,4 +69,15 @@ interface IProfile {
5769
*/
5870

5971
function registerProfile(CandidateProfile memory profile) external;
72+
73+
/**
74+
* @notice The candidate admin changes the public key.
75+
*
76+
* @dev Requirements:
77+
* - The profile must be existed.
78+
* - Only user with candidate admin role can call this method.
79+
* - New public key must not be duplicated.
80+
*/
81+
82+
function changePubkey(address id, bytes memory pubkey) external;
6083
}

contracts/ronin/profile/Profile.sol

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
// SPDX-License-Identifier: MIT
22

33
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
4+
import "../../interfaces/validator/ICandidateManager.sol";
45
import "../../interfaces/validator/IRoninValidatorSet.sol";
56
import "../../interfaces/IProfile.sol";
67
import { ErrUnauthorized, RoleAccess } from "../../utils/CommonErrors.sol";
7-
import "./ProfileStorage.sol";
8+
import { ContractType } from "../../utils/ContractType.sol";
9+
import "./ProfileHandler.sol";
810

911
pragma solidity ^0.8.9;
1012

11-
contract Profile is IProfile, ProfileStorage, Initializable {
13+
contract Profile is IProfile, ProfileHandler, Initializable {
1214
constructor() {
1315
_disableInitializers();
1416
}
@@ -17,6 +19,68 @@ contract Profile is IProfile, ProfileStorage, Initializable {
1719
_setContract(ContractType.VALIDATOR, validatorContract);
1820
}
1921

22+
function migrateTestnet() external {
23+
require(block.chainid == 2021, "mismatch chainID");
24+
require(msg.sender == 0x968D0Cd7343f711216817E617d3f92a23dC91c07, "not testnet admin");
25+
26+
CandidateProfile storage _profile;
27+
28+
address[10] memory consensusList = [
29+
0xCaba9D9424D6bAD99CE352A943F59279B533417a,
30+
0x9f1Abc67beA4db5560371fF3089F4Bfe934c36Bc,
31+
0xA85ddDdCeEaB43DccAa259dd4936aC104386F9aa,
32+
0xAcf8Bf98D1632e602d0B1761771049aF21dd6597,
33+
0xE9bf2A788C27dADc6B169d52408b710d267b9bff,
34+
0xD086D2e3Fac052A3f695a4e8905Ce1722531163C,
35+
// 0x9687e8C41fa369aD08FD278a43114C4207856a61, // missing
36+
0xa325Fd3a2f4f5CafE2c151eE428b5CeDeD628193,
37+
0x9422d990AcDc3f2b3AA3B97303aD3060F09d7ffC,
38+
0xc3C97512421BF3e339E9fd412f18584e53138bFA,
39+
0x78fD38faa30ea66702cc39383D2E84f9a4A56fA6
40+
];
41+
42+
for (uint i; i < consensusList.length; i++) {
43+
_migrateTestnetHelper(consensusList[i]);
44+
}
45+
46+
{
47+
_profile = _getId2ProfileHelper(0xCaba9D9424D6bAD99CE352A943F59279B533417a);
48+
_setGovernor(_profile, 0xb033ba62EC622dC54D0ABFE0254e79692147CA26);
49+
}
50+
{
51+
_profile = _getId2ProfileHelper(0x9f1Abc67beA4db5560371fF3089F4Bfe934c36Bc);
52+
_setGovernor(_profile, 0x087D08e3ba42e64E3948962dd1371F906D1278b9);
53+
}
54+
{
55+
_profile = _getId2ProfileHelper(0xA85ddDdCeEaB43DccAa259dd4936aC104386F9aa);
56+
_setGovernor(_profile, 0x52ec2e6BBcE45AfFF8955Da6410bb13812F4289F);
57+
}
58+
{
59+
_profile = _getId2ProfileHelper(0xAcf8Bf98D1632e602d0B1761771049aF21dd6597);
60+
_setGovernor(_profile, 0xd24D87DDc1917165435b306aAC68D99e0F49A3Fa);
61+
}
62+
}
63+
64+
function migrateTestnetManual(address consensus, address governor) external {
65+
require(block.chainid == 2021, "mismatch chainID");
66+
require(msg.sender == 0x968D0Cd7343f711216817E617d3f92a23dC91c07, "not testnet admin");
67+
68+
_migrateTestnetHelper(consensus);
69+
if (governor != address(0)) {
70+
CandidateProfile storage _profile = _getId2ProfileHelper(consensus);
71+
_setGovernor(_profile, governor);
72+
}
73+
}
74+
75+
function _migrateTestnetHelper(address consensus) internal {
76+
CandidateProfile storage _profile = _getId2ProfileHelper(consensus);
77+
ICandidateManager.ValidatorCandidate memory info = IRoninValidatorSet(getContract(ContractType.VALIDATOR))
78+
.getCandidateInfo(consensus);
79+
_setConsensus(_profile, consensus);
80+
_setAdmin(_profile, info.admin);
81+
_setTreasury(_profile, payable(info.treasuryAddr));
82+
}
83+
2084
/**
2185
* @inheritdoc IProfile
2286
*/
@@ -37,13 +101,28 @@ contract Profile is IProfile, ProfileStorage, Initializable {
37101
* @inheritdoc IProfile
38102
*/
39103
function registerProfile(CandidateProfile memory profile) external {
104+
if (profile.id != profile.consensus) revert ErrIdAndConsensusDiffer();
105+
40106
CandidateProfile storage _profile = _id2Profile[profile.id];
41107
if (_profile.id != address(0)) revert ErrExistentProfile();
42108
if (
43109
msg.sender != profile.admin ||
44110
!IRoninValidatorSet(getContract(ContractType.VALIDATOR)).isCandidateAdmin(profile.consensus, profile.admin)
45111
) revert ErrUnauthorized(msg.sig, RoleAccess.ADMIN);
112+
_checkDuplicatedInRegistry(profile);
46113

47114
_addNewProfile(_profile, profile);
48115
}
116+
117+
/**
118+
* @inheritdoc IProfile
119+
*/
120+
function changePubkey(address id, bytes memory pubkey) external {
121+
CandidateProfile storage _profile = _getId2ProfileHelper(id);
122+
if (msg.sender != _profile.admin) revert ErrUnauthorized(msg.sig, RoleAccess.ADMIN);
123+
_checkNonDuplicatedPubkey(pubkey);
124+
_setPubkey(_profile, pubkey);
125+
126+
emit PubkeyChanged(id, pubkey);
127+
}
49128
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.9;
4+
5+
import "../../utils/RoleAccess.sol";
6+
import { ProfileStorage } from "./ProfileStorage.sol";
7+
8+
abstract contract ProfileHandler is ProfileStorage {
9+
/**
10+
* @dev Checks each element in the new profile and reverts if there is duplication with any existing profile.
11+
*/
12+
function _checkDuplicatedInRegistry(CandidateProfile memory profile) internal view {
13+
_checkNonZeroAndNonDuplicated(RoleAccess.CONSENSUS, profile.consensus);
14+
_checkNonZeroAndNonDuplicated(RoleAccess.CANDIDATE_ADMIN, profile.admin);
15+
_checkNonZeroAndNonDuplicated(RoleAccess.TREASURY, profile.treasury);
16+
_checkNonDuplicated(RoleAccess.TREASURY, profile.governor);
17+
_checkNonDuplicatedPubkey(profile.pubkey);
18+
}
19+
20+
function _checkNonZeroAndNonDuplicated(RoleAccess addressType, address addr) internal view {
21+
if (addr == address(0)) revert ErrZeroAddress(addressType);
22+
_checkNonDuplicated(addressType, addr);
23+
}
24+
25+
function _checkNonDuplicated(RoleAccess addressType, address addr) internal view {
26+
if (_registry[uint256(uint160(addr))]) {
27+
revert ErrDuplicatedInfo(addressType, uint256(uint160(addr)));
28+
}
29+
}
30+
31+
function _checkNonDuplicatedPubkey(bytes memory pubkey) internal view {
32+
if (_registry[_hashPubkey(pubkey)]) {
33+
revert ErrDuplicatedPubkey(pubkey);
34+
}
35+
}
36+
}

contracts/ronin/profile/ProfileStorage.sol

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,72 @@ import { IProfile } from "../../interfaces/IProfile.sol";
88
abstract contract ProfileStorage is IProfile, HasContracts {
99
/// @dev Mapping from id address => candidate profile.
1010
mapping(address => CandidateProfile) internal _id2Profile;
11+
/**
12+
* @dev Mapping from any address or keccak256(pubkey) => whether it is already registered.
13+
* This registry can only be toggled to `true` and NOT vice versa. All registered values
14+
* cannot be reused.
15+
*/
16+
mapping(uint256 => bool) internal _registry;
1117
/// @dev Upgradeable gap.
12-
bytes32[50] __gap;
18+
bytes32[49] __gap;
1319

1420
/**
1521
* @dev Add a profile from memory to storage.
1622
*/
1723
function _addNewProfile(CandidateProfile storage _profile, CandidateProfile memory newProfile) internal {
1824
_profile.id = newProfile.id;
19-
_profile.consensus = newProfile.consensus;
20-
_profile.admin = newProfile.admin;
21-
_profile.treasury = newProfile.treasury;
22-
_profile.governor = newProfile.governor;
23-
_profile.pubkey = newProfile.pubkey;
25+
26+
_setConsensus(_profile, newProfile.consensus);
27+
_setAdmin(_profile, newProfile.admin);
28+
_setTreasury(_profile, newProfile.treasury);
29+
_setGovernor(_profile, newProfile.governor);
30+
_setPubkey(_profile, newProfile.pubkey);
2431

2532
emit ProfileAdded(newProfile.id);
2633
}
2734

35+
function _setConsensus(CandidateProfile storage _profile, address consensus) internal {
36+
_profile.consensus = consensus;
37+
_registry[uint256(uint160(consensus))] = true;
38+
}
39+
40+
function _setAdmin(CandidateProfile storage _profile, address admin) internal {
41+
_profile.admin = admin;
42+
_registry[uint256(uint160(admin))] = true;
43+
}
44+
45+
function _setTreasury(CandidateProfile storage _profile, address payable treasury) internal {
46+
_profile.treasury = treasury;
47+
_registry[uint256(uint160(address(treasury)))] = true;
48+
}
49+
2850
/**
29-
* @dev Get an existed profile struct from id. Revert if the profile does not exists.
51+
* @dev Allow to registry a profile without governor address since not all validators are governing validators.
52+
*/
53+
function _setGovernor(CandidateProfile storage _profile, address governor) internal {
54+
_profile.governor = governor;
55+
if (governor != address(0)) {
56+
_registry[uint256(uint160(governor))] = true;
57+
}
58+
}
59+
60+
function _setPubkey(CandidateProfile storage _profile, bytes memory pubkey) internal {
61+
_profile.pubkey = pubkey;
62+
_registry[_hashPubkey(pubkey)] = true;
63+
}
64+
65+
/**
66+
* @dev Get an existed profile struct from `id`. Revert if the profile does not exists.
3067
*/
3168
function _getId2ProfileHelper(address id) internal view returns (CandidateProfile storage _profile) {
3269
_profile = _id2Profile[id];
3370
if (_profile.id == address(0)) revert ErrNonExistentProfile();
3471
}
72+
73+
/**
74+
* @dev Returns hash of a public key.
75+
*/
76+
function _hashPubkey(bytes memory pubkey) internal pure returns (uint256) {
77+
return uint256(keccak256(pubkey));
78+
}
3579
}

contracts/utils/RoleAccess.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@ enum RoleAccess {
1010
/* 5 */ WITHDRAWAL_MIGRATOR,
1111
/* 6 */ __DEPRECATED_BRIDGE_OPERATOR,
1212
/* 7 */ BLOCK_PRODUCER,
13-
/* 8 */ VALIDATOR_CANDIDATE
13+
/* 8 */ VALIDATOR_CANDIDATE,
14+
/* 9 */ CONSENSUS,
15+
/* 10 */ TREASURY
1416
}

0 commit comments

Comments
 (0)