Skip to content

Commit

Permalink
v0.4.4 (#194)
Browse files Browse the repository at this point in the history
  • Loading branch information
nxqbao authored Mar 8, 2023
2 parents c712835 + 9424877 commit 66903db
Show file tree
Hide file tree
Showing 9 changed files with 820 additions and 277 deletions.
5 changes: 5 additions & 0 deletions contracts/interfaces/IBridgeTracking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
pragma solidity ^0.8.0;

interface IBridgeTracking {
struct Request {
VoteKind kind;
uint256 id;
}

enum VoteKind {
Deposit,
Withdrawal,
Expand Down
55 changes: 55 additions & 0 deletions contracts/mocks/MockGatewayForTracking.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.9;

import "../interfaces/IBridgeTracking.sol";
import "../interfaces/collections/IHasBridgeTrackingContract.sol";

contract MockGatewayForTracking is IHasBridgeTrackingContract {
/// @dev The bridge tracking contract
IBridgeTracking internal _bridgeTrackingContract;

constructor(address __bridgeTrackingContract) {
_setBridgeTrackingContract(__bridgeTrackingContract);
}

/**
* @inheritdoc IHasBridgeTrackingContract
*/
function bridgeTrackingContract() external view returns (address) {
return address(_bridgeTrackingContract);
}

/**
* @inheritdoc IHasBridgeTrackingContract
*/
function setBridgeTrackingContract(address _addr) external {
require(_addr.code.length > 0, "RoninGatewayV2: set to non-contract");
_setBridgeTrackingContract(_addr);
}

/**
* @dev Sets the bridge tracking contract.
*
* Emits the event `BridgeTrackingContractUpdated`.
*
*/
function _setBridgeTrackingContract(address _addr) internal {
_bridgeTrackingContract = IBridgeTracking(_addr);
emit BridgeTrackingContractUpdated(_addr);
}

function sendBallot(
IBridgeTracking.VoteKind _kind,
uint256 _id,
address[] memory _voters
) external {
for (uint256 _i; _i < _voters.length; _i++) {
_bridgeTrackingContract.recordVote(_kind, _id, _voters[_i]);
}
}

function sendApprovedVote(IBridgeTracking.VoteKind _kind, uint256 _id) external {
_bridgeTrackingContract.handleVoteApproved(_kind, _id);
}
}
162 changes: 93 additions & 69 deletions contracts/ronin/BridgeTracking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,40 @@ import "../extensions/collections/HasValidatorContract.sol";
import "../interfaces/IBridgeTracking.sol";

contract BridgeTracking is HasBridgeContract, HasValidatorContract, Initializable, IBridgeTracking {
struct VoteStats {
uint256 totalVotes;
struct PeriodVotingMetric {
/// @dev Total requests that are tracked in the period. This value is 0 until the {_bufferMetric.requests[]} gets added into a period metric.
uint256 totalRequests;
uint256 totalBallots;
mapping(address => uint256) totalBallotsOf;
address[] voters;
}

struct VoteStatsTimeWrapper {
struct PeriodVotingMetricTimeWrapper {
uint256 lastEpoch;
VoteStats info;
Request[] requests;
PeriodVotingMetric data;
}

struct ReceiptStats {
// The period that the receipt is approved
struct ReceiptTrackingInfo {
/// @dev The period that the receipt is approved. Value 0 means the receipt is not approved yet.
uint256 approvedPeriod;
// The address list of voters
/// @dev The address list of voters
address[] voters;
// Mapping from voter => flag indicating the voter casts vote for this receipt
/// @dev Mapping from voter => flag indicating the voter casts vote for this receipt
mapping(address => bool) voted;
/// @dev The period that the receipt is tracked, i.e. the metric is transferred from buffer to the period. Value 0 means the receipt is currently in buffer or not tracked yet.
uint256 trackedPeriod;
}

/// @dev Deprecated slots.
uint256[6] private __deprecated;

/// @dev The block that the contract allows incoming mutable calls.
uint256 public startedAtBlock;

/// @dev The temporary info of votes and ballots
VoteStatsTimeWrapper internal _temporaryStats;
PeriodVotingMetricTimeWrapper internal _bufferMetric;
/// @dev Mapping from period number => vote stats based on period
mapping(uint256 => VoteStats) internal _periodStats;
mapping(uint256 => PeriodVotingMetric) internal _periodMetric;
/// @dev Mapping from vote kind => receipt id => receipt stats
mapping(VoteKind => mapping(uint256 => ReceiptStats)) internal _receiptStats;
mapping(VoteKind => mapping(uint256 => ReceiptTrackingInfo)) internal _receiptTrackingInfo;

modifier skipOnUnstarted() {
if (block.number < startedAtBlock) {
Expand Down Expand Up @@ -70,23 +71,19 @@ contract BridgeTracking is HasBridgeContract, HasValidatorContract, Initializabl
* @inheritdoc IBridgeTracking
*/
function totalVotes(uint256 _period) external view override returns (uint256 _totalVotes) {
_totalVotes = _periodStats[_period].totalVotes;

bool _mustCountLastStats = _isLastStatsCountedForPeriod(_period);
if (_mustCountLastStats) {
_totalVotes += _temporaryStats.info.totalVotes;
_totalVotes = _periodMetric[_period].totalRequests;
if (_isBufferCountedForPeriod(_period)) {
_totalVotes += _bufferMetric.requests.length;
}
}

/**
* @inheritdoc IBridgeTracking
*/
function totalBallots(uint256 _period) external view override returns (uint256 _totalBallots) {
_totalBallots = _periodStats[_period].totalBallots;

bool _mustCountLastStats = _isLastStatsCountedForPeriod(_period);
if (_mustCountLastStats) {
_totalBallots += _temporaryStats.info.totalBallots;
_totalBallots = _periodMetric[_period].totalBallots;
if (_isBufferCountedForPeriod(_period)) {
_totalBallots += _bufferMetric.data.totalBallots;
}
}

Expand All @@ -100,38 +97,41 @@ contract BridgeTracking is HasBridgeContract, HasValidatorContract, Initializabl
returns (uint256[] memory _res)
{
_res = new uint256[](_bridgeOperators.length);
bool _mustCountLastStats = _isLastStatsCountedForPeriod(_period);
bool _isBufferCounted = _isBufferCountedForPeriod(_period);
for (uint _i = 0; _i < _bridgeOperators.length; _i++) {
_res[_i] = _totalBallotsOf(_period, _bridgeOperators[_i], _mustCountLastStats);
_res[_i] = _totalBallotsOf(_period, _bridgeOperators[_i], _isBufferCounted);
}
}

/**
* @inheritdoc IBridgeTracking
*/
function totalBallotsOf(uint256 _period, address _bridgeOperator) public view override returns (uint256) {
return _totalBallotsOf(_period, _bridgeOperator, _isLastStatsCountedForPeriod(_period));
return _totalBallotsOf(_period, _bridgeOperator, _isBufferCountedForPeriod(_period));
}

/**
* @inheritdoc IBridgeTracking
*/
function handleVoteApproved(VoteKind _kind, uint256 _requestId) external override onlyBridgeContract skipOnUnstarted {
ReceiptStats storage _stats = _receiptStats[_kind][_requestId];
ReceiptTrackingInfo storage _receiptInfo = _receiptTrackingInfo[_kind][_requestId];

// Only records for the receipt which not approved
if (_stats.approvedPeriod == 0) {
_trySyncPeriodStats();
if (_receiptInfo.approvedPeriod == 0) {
_trySyncBuffer();
uint256 _currentPeriod = _validatorContract.currentPeriod();
_temporaryStats.info.totalVotes++;
_stats.approvedPeriod = _currentPeriod;
_receiptInfo.approvedPeriod = _currentPeriod;

Request storage _bufferRequest = _bufferMetric.requests.push();
_bufferRequest.kind = _kind;
_bufferRequest.id = _requestId;

address[] storage _voters = _stats.voters;
address[] storage _voters = _receiptInfo.voters;
for (uint _i = 0; _i < _voters.length; _i++) {
_increaseBallot(_kind, _requestId, _voters[_i], _currentPeriod);
}

delete _stats.voters;
delete _receiptInfo.voters;
}
}

Expand All @@ -144,41 +144,51 @@ contract BridgeTracking is HasBridgeContract, HasValidatorContract, Initializabl
address _operator
) external override onlyBridgeContract skipOnUnstarted {
uint256 _period = _validatorContract.currentPeriod();
_trySyncPeriodStats();
ReceiptStats storage _stats = _receiptStats[_kind][_requestId];
_trySyncBuffer();
ReceiptTrackingInfo storage _receiptInfo = _receiptTrackingInfo[_kind][_requestId];

// Stores the ones vote for the (deposit/mainchain withdrawal) request which is not approved yet
if (_stats.approvedPeriod == 0) {
_stats.voters.push(_operator);
// When the vote is not approved yet, the voters are saved in the receipt info, and not increase ballot metric.
// The ballot metric will be increased later in the {handleVoteApproved} method.
if (_receiptInfo.approvedPeriod == 0) {
_receiptInfo.voters.push(_operator);
return;
}

_increaseBallot(_kind, _requestId, _operator, _period);
}

/**
* Increases the ballot for the operator at a period.
* @dev Increases the ballot for the operator at a period.
*/
function _increaseBallot(
VoteKind _kind,
uint256 _requestId,
address _operator,
uint256 _period
uint256 _currentPeriod
) internal {
ReceiptStats storage _receiptInfo = _receiptStats[_kind][_requestId];
ReceiptTrackingInfo storage _receiptInfo = _receiptTrackingInfo[_kind][_requestId];
if (_receiptInfo.voted[_operator]) {
return;
}

_receiptInfo.voted[_operator] = true;

// Only records within a period
if (_receiptInfo.approvedPeriod == _period) {
if (_temporaryStats.info.totalBallotsOf[_operator] == 0) {
_temporaryStats.info.voters.push(_operator);
uint256 _trackedPeriod = _receiptInfo.trackedPeriod;

// Do not increase ballot for receipt that is neither in the buffer, nor in the most current tracked period.
// If the receipt is not tracked in a period, increase metric in buffer.
if (_trackedPeriod == 0) {
if (_bufferMetric.data.totalBallotsOf[_operator] == 0) {
_bufferMetric.data.voters.push(_operator);
}
_temporaryStats.info.totalBallots++;
_temporaryStats.info.totalBallotsOf[_operator]++;
_bufferMetric.data.totalBallots++;
_bufferMetric.data.totalBallotsOf[_operator]++;
}
// If the receipt is tracked in the most current tracked period, increase metric in the period.
else if (_trackedPeriod == _currentPeriod) {
PeriodVotingMetric storage _metric = _periodMetric[_trackedPeriod];
_metric.totalBallots++;
_metric.totalBallotsOf[_operator]++;
}
}

Expand All @@ -190,42 +200,56 @@ contract BridgeTracking is HasBridgeContract, HasValidatorContract, Initializabl
address _bridgeOperator,
bool _mustCountLastStats
) internal view returns (uint256 _totalBallots) {
_totalBallots = _periodStats[_period].totalBallotsOf[_bridgeOperator];
_totalBallots = _periodMetric[_period].totalBallotsOf[_bridgeOperator];
if (_mustCountLastStats) {
_totalBallots += _temporaryStats.info.totalBallotsOf[_bridgeOperator];
_totalBallots += _bufferMetric.data.totalBallotsOf[_bridgeOperator];
}
}

/**
* @dev Syncs period stats if the last epoch + 1 is already wrapped up.
* @dev Syncs period stats. Move all data from the buffer metric to the period metric.
*
* Requirements:
* - The epoch after the buffer epoch is wrapped up.
*/
function _trySyncPeriodStats() internal {
function _trySyncBuffer() internal {
uint256 _currentEpoch = _validatorContract.epochOf(block.number);
if (_temporaryStats.lastEpoch < _currentEpoch) {
(, uint256 _period) = _validatorContract.tryGetPeriodOfEpoch(_temporaryStats.lastEpoch + 1);
VoteStats storage _stats = _periodStats[_period];
_stats.totalVotes += _temporaryStats.info.totalVotes;
_stats.totalBallots += _temporaryStats.info.totalBallots;

address _voter;
for (uint _i = 0; _i < _temporaryStats.info.voters.length; _i++) {
_voter = _temporaryStats.info.voters[_i];
_stats.totalBallotsOf[_voter] += _temporaryStats.info.totalBallotsOf[_voter];
delete _temporaryStats.info.totalBallotsOf[_voter];
if (_bufferMetric.lastEpoch < _currentEpoch) {
(, uint256 _trackedPeriod) = _validatorContract.tryGetPeriodOfEpoch(_bufferMetric.lastEpoch + 1);
_bufferMetric.lastEpoch = _currentEpoch;

// Copy numbers of totals
PeriodVotingMetric storage _metric = _periodMetric[_trackedPeriod];
_metric.totalRequests += _bufferMetric.requests.length;
_metric.totalBallots += _bufferMetric.data.totalBallots;

// Copy voters info and voters' ballot
for (uint _i = 0; _i < _bufferMetric.data.voters.length; _i++) {
address _voter = _bufferMetric.data.voters[_i];
_metric.totalBallotsOf[_voter] += _bufferMetric.data.totalBallotsOf[_voter];
delete _bufferMetric.data.totalBallotsOf[_voter]; // need to manually delete each element, due to mapping
}
delete _temporaryStats.info;
_temporaryStats.lastEpoch = _currentEpoch;

// Mark all receipts in the buffer as tracked. Keep total number of receipts and delete receipt details.
for (uint _i = 0; _i < _bufferMetric.requests.length; _i++) {
Request storage _bufferRequest = _bufferMetric.requests[_i];
ReceiptTrackingInfo storage _receiptInfo = _receiptTrackingInfo[_bufferRequest.kind][_bufferRequest.id];
_receiptInfo.trackedPeriod = _trackedPeriod;
}

delete _bufferMetric.requests;
delete _bufferMetric.data;
}
}

/**
* @dev Returns whether the last stats must be counted or not;
* @dev Returns whether the buffer stats must be counted or not.
*/
function _isLastStatsCountedForPeriod(uint256 _queriedPeriod) internal view returns (bool) {
function _isBufferCountedForPeriod(uint256 _queriedPeriod) internal view returns (bool) {
uint256 _currentEpoch = _validatorContract.epochOf(block.number);
(bool _filled, uint256 _periodOfNextTemporaryEpoch) = _validatorContract.tryGetPeriodOfEpoch(
_temporaryStats.lastEpoch + 1
_bufferMetric.lastEpoch + 1
);
return _filled && _queriedPeriod == _periodOfNextTemporaryEpoch && _temporaryStats.lastEpoch < _currentEpoch;
return _filled && _queriedPeriod == _periodOfNextTemporaryEpoch && _bufferMetric.lastEpoch < _currentEpoch;
}
}
1 change: 1 addition & 0 deletions contracts/ronin/staking/CandidateStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ abstract contract CandidateStaking is BaseStaking, ICandidateStaking, Percentage
uint256 _commissionRate
) external payable override nonReentrant {
if (isAdminOfActivePool(msg.sender)) revert ErrAdminOfAnyActivePoolForbidden(msg.sender);
if (_commissionRate > _maxCommissionRate) revert ErrInvalidCommissionRate();

uint256 _amount = msg.value;
address payable _poolAdmin = payable(msg.sender);
Expand Down
Loading

0 comments on commit 66903db

Please sign in to comment.