Skip to content

Commit

Permalink
feat: add agent address for validators (#568)
Browse files Browse the repository at this point in the history
* feat: add agent address for validators

* fix: add new Error

* fix: use delete instead of set address(0)

* feat: add ut

* feat: rename _getMsgSender to _bep410MsgSender
  • Loading branch information
cosinlink authored Jul 17, 2024
1 parent 0fa2e80 commit f2617f8
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 9 deletions.
56 changes: 47 additions & 9 deletions contracts/BC_fusion/StakeHub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ contract StakeHub is System, Initializable, Protectable {
error ConsensusAddressExpired();
// @notice signature: 0x0d7b78d4
error InvalidSynPackage();
// @notice signature: 0xbebdc757
error InvalidAgent();
// @notice signature: 0x682a6e7c
error InvalidValidator();

/*----------------- storage -----------------*/
uint8 private _receiveFundStatus;
Expand Down Expand Up @@ -135,6 +139,9 @@ contract StakeHub is System, Initializable, Protectable {
// slash key => slash jail time
mapping(bytes32 => uint256) private _felonyRecords;

// agent => validator operator address
mapping(address => address) public agentToOperator;

/*----------------- structs and events -----------------*/
struct StakeMigrationPackage {
address operatorAddress; // the operator address of the target validator to delegate to
Expand Down Expand Up @@ -162,7 +169,9 @@ contract StakeHub is System, Initializable, Protectable {
bool jailed;
uint256 jailUntil;
uint256 updateTime;
uint256[20] __reservedSlots;
// The agent can perform transactions on behalf of the operatorAddress in certain scenarios.
address agent;
uint256[19] __reservedSlots;
}

struct Description {
Expand Down Expand Up @@ -219,6 +228,7 @@ contract StakeHub is System, Initializable, Protectable {
address indexed operatorAddress, address indexed delegator, uint256 bnbAmount, StakeMigrationRespCode respCode
);
event UnexpectedPackage(uint8 channelId, bytes msgBytes);
event AgentChanged(address indexed operatorAddress, address indexed oldAgent, address indexed newAgent);

/*----------------- modifiers -----------------*/
modifier validatorExist(address operatorAddress) {
Expand Down Expand Up @@ -315,6 +325,24 @@ contract StakeHub is System, Initializable, Protectable {
}

/*----------------- external functions -----------------*/
function updateAgent(address newAgent) external validatorExist(msg.sender) {
if (agentToOperator[newAgent] != address(0)) revert InvalidAgent();
if (_validatorSet.contains(newAgent)) revert InvalidAgent();

address operatorAddress = msg.sender;
address oldAgent = _validators[operatorAddress].agent;
if (oldAgent == newAgent) revert InvalidAgent();

if (oldAgent != address(0)) {
delete agentToOperator[oldAgent];
}

_validators[operatorAddress].agent = newAgent;
agentToOperator[newAgent] = operatorAddress;

emit AgentChanged(operatorAddress, oldAgent, newAgent);
}

/**
* @param consensusAddress the consensus address of the validator
* @param voteAddress the vote address of the validator
Expand All @@ -332,6 +360,8 @@ contract StakeHub is System, Initializable, Protectable {
// basic check
address operatorAddress = msg.sender;
if (_validatorSet.contains(operatorAddress)) revert ValidatorExisted();
if (agentToOperator[operatorAddress] != address(0)) revert InvalidValidator();

if (consensusToOperator[consensusAddress] != address(0) || _legacyConsensusAddress[consensusAddress]) {
revert DuplicateConsensusAddress();
}
Expand Down Expand Up @@ -384,14 +414,14 @@ contract StakeHub is System, Initializable, Protectable {
external
whenNotPaused
notInBlackList
validatorExist(msg.sender)
validatorExist(_bep410MsgSender())
{
if (newConsensusAddress == address(0)) revert InvalidConsensusAddress();
if (consensusToOperator[newConsensusAddress] != address(0) || _legacyConsensusAddress[newConsensusAddress]) {
revert DuplicateConsensusAddress();
}

address operatorAddress = msg.sender;
address operatorAddress = _bep410MsgSender();
Validator storage valInfo = _validators[operatorAddress];
if (valInfo.updateTime + BREATHE_BLOCK_INTERVAL > block.timestamp) revert UpdateTooFrequently();

Expand All @@ -410,9 +440,9 @@ contract StakeHub is System, Initializable, Protectable {
external
whenNotPaused
notInBlackList
validatorExist(msg.sender)
validatorExist(_bep410MsgSender())
{
address operatorAddress = msg.sender;
address operatorAddress = _bep410MsgSender();
Validator storage valInfo = _validators[operatorAddress];
if (valInfo.updateTime + BREATHE_BLOCK_INTERVAL > block.timestamp) revert UpdateTooFrequently();

Expand All @@ -436,9 +466,9 @@ contract StakeHub is System, Initializable, Protectable {
external
whenNotPaused
notInBlackList
validatorExist(msg.sender)
validatorExist(_bep410MsgSender())
{
address operatorAddress = msg.sender;
address operatorAddress = _bep410MsgSender();
Validator storage valInfo = _validators[operatorAddress];
if (valInfo.updateTime + BREATHE_BLOCK_INTERVAL > block.timestamp) revert UpdateTooFrequently();

Expand All @@ -456,9 +486,9 @@ contract StakeHub is System, Initializable, Protectable {
function editVoteAddress(
bytes calldata newVoteAddress,
bytes calldata blsProof
) external whenNotPaused notInBlackList validatorExist(msg.sender) {
) external whenNotPaused notInBlackList validatorExist(_bep410MsgSender()) {
// proof-of-possession verify
address operatorAddress = msg.sender;
address operatorAddress = _bep410MsgSender();
if (!_checkVoteAddress(operatorAddress, newVoteAddress, blsProof)) revert InvalidVoteAddress();
if (voteToOperator[newVoteAddress] != address(0) || _legacyVoteAddress[newVoteAddress]) {
revert DuplicateVoteAddress();
Expand Down Expand Up @@ -1179,4 +1209,12 @@ contract StakeHub is System, Initializable, Protectable {
uint256 bnbAmount = IStakeCredit(_validators[operatorAddress].creditContract).claim(msg.sender, requestNumber);
emit Claimed(operatorAddress, msg.sender, bnbAmount);
}

function _bep410MsgSender() internal view returns (address) {
if (agentToOperator[msg.sender] != address(0)) {
return agentToOperator[msg.sender];
}

return msg.sender;
}
}
70 changes: 70 additions & 0 deletions test/StakeHub.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ contract StakeHubTest is Deployer {
event MigrateFailed(
address indexed operatorAddress, address indexed delegator, uint256 bnbAmount, StakeMigrationRespCode respCode
);
event AgentChanged(address indexed operatorAddress, address indexed oldAgent, address indexed newAgent);

enum StakeMigrationRespCode {
MIGRATE_SUCCESS,
Expand Down Expand Up @@ -726,4 +727,73 @@ contract StakeHubTest is Deployer {
elements[1] = vals.encodeList();
return elements.encodeList();
}

function testAgent() external {
// create validator
(address validator,,,) = _createValidator(2000 ether);
vm.startPrank(validator);

// edit failed because of `UpdateTooFrequently`
vm.expectRevert(StakeHub.UpdateTooFrequently.selector);
stakeHub.editConsensusAddress(address(1));

// update agent
address newAgent = validator;
vm.expectRevert(StakeHub.InvalidAgent.selector);
stakeHub.updateAgent(newAgent);

newAgent = address(0x1234);
vm.expectEmit(true, true, false, true, address(stakeHub));
emit AgentChanged(validator, address(0), newAgent);
stakeHub.updateAgent(newAgent);

vm.stopPrank();

vm.startPrank(newAgent);
// edit consensus address
vm.warp(block.timestamp + 1 days);
address newConsensusAddress = address(0x1234);
vm.expectEmit(true, true, false, true, address(stakeHub));
emit ConsensusAddressEdited(validator, newConsensusAddress);
stakeHub.editConsensusAddress(newConsensusAddress);
address realConsensusAddr = stakeHub.getValidatorConsensusAddress(validator);
assertEq(realConsensusAddr, newConsensusAddress);

// edit commission rate
vm.warp(block.timestamp + 1 days);
vm.expectRevert(StakeHub.InvalidCommission.selector);
stakeHub.editCommissionRate(110);
vm.expectRevert(StakeHub.InvalidCommission.selector);
stakeHub.editCommissionRate(16);
vm.expectEmit(true, false, false, true, address(stakeHub));
emit CommissionRateEdited(validator, 15);
stakeHub.editCommissionRate(15);
StakeHub.Commission memory realComm = stakeHub.getValidatorCommission(validator);
assertEq(realComm.rate, 15);

// edit description
vm.warp(block.timestamp + 1 days);
StakeHub.Description memory description = stakeHub.getValidatorDescription(validator);
description.moniker = "Test";
description.website = "Test";
vm.expectEmit(true, false, false, true, address(stakeHub));
emit DescriptionEdited(validator);
stakeHub.editDescription(description);
StakeHub.Description memory realDesc = stakeHub.getValidatorDescription(validator);
assertNotEq(realDesc.moniker, "Test"); // edit moniker will be ignored
assertEq(realDesc.website, "Test");

// edit vote address
vm.warp(block.timestamp + 1 days);
bytes memory newVoteAddress =
hex"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001234";
bytes memory blsProof = new bytes(96);
vm.expectEmit(true, false, false, true, address(stakeHub));
emit VoteAddressEdited(validator, newVoteAddress);
stakeHub.editVoteAddress(newVoteAddress, blsProof);
bytes memory realVoteAddr = stakeHub.getValidatorVoteAddress(validator);
assertEq(realVoteAddr, newVoteAddress);

vm.stopPrank();
}
}
6 changes: 6 additions & 0 deletions test/utils/interface/IStakeHub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ interface StakeHub {
error ValidatorNotJailed();
error VoteAddressExpired();
error ZeroShares();
error InvalidAgent();
error InvalidValidator();

event BlackListed(address indexed target);
event Claimed(address indexed operatorAddress, address indexed delegator, uint256 bnbAmount);
Expand Down Expand Up @@ -94,6 +96,7 @@ interface StakeHub {
);
event ValidatorUnjailed(address indexed operatorAddress);
event VoteAddressEdited(address indexed operatorAddress, bytes newVoteAddress);
event AgentChanged(address indexed operatorAddress, address indexed oldAgent, address indexed newAgent);

receive() external payable;

Expand Down Expand Up @@ -179,4 +182,7 @@ interface StakeHub {
function updateParam(string memory key, bytes memory value) external;
function voteExpiration(bytes memory) external view returns (uint256);
function voteToOperator(bytes memory) external view returns (address);

function agentToOperator(address) external view returns (address);
function updateAgent(address newAgent) external;
}

0 comments on commit f2617f8

Please sign in to comment.