Skip to content

Commit

Permalink
Merge pull request #2 from ronin-chain/fix/handle-vote-on-bulkDepositFor
Browse files Browse the repository at this point in the history
fix: handle vote on bulk deposit for
  • Loading branch information
nxqbao authored Mar 6, 2024
2 parents 1e56662 + 1274c9c commit 1be539e
Show file tree
Hide file tree
Showing 6 changed files with 313 additions and 98 deletions.
114 changes: 51 additions & 63 deletions src/ronin/gateway/RoninGatewayV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ contract RoninGatewayV3 is
* @dev Reverts if the method caller is not bridge operator.
*/
function _requireBridgeOperator() internal view {
if (!IBridgeManager(getContract(ContractType.BRIDGE_MANAGER)).isBridgeOperator(msg.sender))
if (!IBridgeManager(getContract(ContractType.BRIDGE_MANAGER)).isBridgeOperator(msg.sender)) {
revert ErrUnauthorized(msg.sig, RoleAccess.__DEPRECATED_BRIDGE_OPERATOR);
}
}

/**
Expand All @@ -88,7 +89,7 @@ contract RoninGatewayV3 is
uint256 _denominator,
uint256 _trustedNumerator,
uint256 _trustedDenominator,
address[] calldata /* _withdrawalMigrators */,
address[] calldata, /* _withdrawalMigrators */
// _packedAddresses[0]: roninTokens
// _packedAddresses[1]: mainchainTokens
address[][2] calldata _packedAddresses,
Expand Down Expand Up @@ -127,7 +128,7 @@ contract RoninGatewayV3 is
address[] calldata operators
) external view returns (bytes[] memory _signatures) {
_signatures = new bytes[](operators.length);
for (uint256 _i = 0; _i < operators.length; ) {
for (uint256 _i = 0; _i < operators.length;) {
_signatures[_i] = _withdrawalSig[withdrawalId][operators[_i]];

unchecked {
Expand All @@ -140,30 +141,26 @@ contract RoninGatewayV3 is
* @inheritdoc IRoninGatewayV3
*/
function depositFor(Transfer.Receipt calldata _receipt) external whenNotPaused onlyBridgeOperator {
address _sender = msg.sender;
_depositFor(_receipt, _sender, minimumVoteWeight());
IBridgeTracking(getContract(ContractType.BRIDGE_TRACKING)).recordVote(
IBridgeTracking.VoteKind.Deposit,
_receipt.id,
_sender
);
_depositFor(_receipt, msg.sender, minimumVoteWeight());
}

/**
* @inheritdoc IRoninGatewayV3
*/
function tryBulkAcknowledgeMainchainWithdrew(
uint256[] calldata _withdrawalIds
) external onlyBridgeOperator returns (bool[] memory _executedReceipts) {
function tryBulkAcknowledgeMainchainWithdrew(uint256[] calldata _withdrawalIds)
external
onlyBridgeOperator
returns (bool[] memory _executedReceipts)
{
address _governor = msg.sender;
uint256 _minVoteWeight = minimumVoteWeight();

uint256 _withdrawalId;
_executedReceipts = new bool[](_withdrawalIds.length);
IBridgeTracking _bridgeTrackingContract = IBridgeTracking(getContract(ContractType.BRIDGE_TRACKING));
for (uint256 _i; _i < _withdrawalIds.length; ) {
IBridgeTracking bridgeTrackingContract = IBridgeTracking(getContract(ContractType.BRIDGE_TRACKING));
for (uint256 _i; _i < _withdrawalIds.length;) {
_withdrawalId = _withdrawalIds[_i];
_bridgeTrackingContract.recordVote(IBridgeTracking.VoteKind.MainchainWithdrawal, _withdrawalId, _governor);
bridgeTrackingContract.recordVote(IBridgeTracking.VoteKind.MainchainWithdrawal, _withdrawalId, _governor);
if (mainchainWithdrew(_withdrawalId)) {
_executedReceipts[_i] = true;
} else {
Expand All @@ -173,7 +170,7 @@ contract RoninGatewayV3 is
VoteStatus _status = _castIsolatedVote(_vote, _governor, _minVoteWeight, _hash);
if (_status == VoteStatus.Approved) {
_vote.status = VoteStatus.Executed;
_bridgeTrackingContract.handleVoteApproved(IBridgeTracking.VoteKind.MainchainWithdrawal, _withdrawalId);
bridgeTrackingContract.handleVoteApproved(IBridgeTracking.VoteKind.MainchainWithdrawal, _withdrawalId);
emit MainchainWithdrew(_hash, _withdrawal);
}
}
Expand All @@ -187,26 +184,22 @@ contract RoninGatewayV3 is
/**
* @inheritdoc IRoninGatewayV3
*/
function tryBulkDepositFor(
Transfer.Receipt[] calldata _receipts
) external whenNotPaused onlyBridgeOperator returns (bool[] memory _executedReceipts) {
address _sender = msg.sender;

Transfer.Receipt memory _receipt;
_executedReceipts = new bool[](_receipts.length);
uint256 _minVoteWeight = minimumVoteWeight();
IBridgeTracking _bridgeTrackingContract = IBridgeTracking(getContract(ContractType.BRIDGE_TRACKING));
for (uint256 _i; _i < _receipts.length; ) {
_receipt = _receipts[_i];
_bridgeTrackingContract.recordVote(IBridgeTracking.VoteKind.Deposit, _receipt.id, _sender);
if (depositVote[_receipt.mainchain.chainId][_receipt.id].status == VoteStatus.Executed) {
_executedReceipts[_i] = true;
function tryBulkDepositFor(Transfer.Receipt[] calldata receipts)
external
whenNotPaused
onlyBridgeOperator
returns (bool[] memory _executedReceipts)
{
_executedReceipts = new bool[](receipts.length);
uint256 minVoteWeight = minimumVoteWeight();

Transfer.Receipt memory iReceipt;
for (uint i; i < receipts.length; ++i) {
iReceipt = receipts[i];
if (depositVote[iReceipt.mainchain.chainId][iReceipt.id].status == VoteStatus.Executed) {
_executedReceipts[i] = true;
} else {
_depositFor(_receipt, _sender, _minVoteWeight);
}

unchecked {
++_i;
_depositFor(iReceipt, msg.sender, minVoteWeight);
}
}
}
Expand All @@ -224,7 +217,7 @@ contract RoninGatewayV3 is
function bulkRequestWithdrawalFor(Transfer.Request[] calldata _requests, uint256 _chainId) external whenNotPaused {
if (_requests.length == 0) revert ErrEmptyArray();

for (uint256 _i; _i < _requests.length; ) {
for (uint256 _i; _i < _requests.length;) {
_requestWithdrawalFor(_requests[_i], msg.sender, _chainId);
unchecked {
++_i;
Expand Down Expand Up @@ -263,7 +256,7 @@ contract RoninGatewayV3 is

uint256 id;
IBridgeTracking _bridgeTrackingContract = IBridgeTracking(getContract(ContractType.BRIDGE_TRACKING));
for (uint256 _i; _i < withdrawals.length; ) {
for (uint256 _i; _i < withdrawals.length;) {
id = withdrawals[_i];
_withdrawalSig[id][operator] = signatures[_i];
_bridgeTrackingContract.recordVote(IBridgeTracking.VoteKind.Withdrawal, id, operator);
Expand Down Expand Up @@ -338,10 +331,11 @@ contract RoninGatewayV3 is
uint256[] calldata _chainIds,
Token.Standard[] calldata _standards
) internal {
if (!(_roninTokens.length == _mainchainTokens.length && _roninTokens.length == _chainIds.length))
if (!(_roninTokens.length == _mainchainTokens.length && _roninTokens.length == _chainIds.length)) {
revert ErrLengthMismatch(msg.sig);
}

for (uint256 _i; _i < _roninTokens.length; ) {
for (uint256 _i; _i < _roninTokens.length;) {
_mainchainToken[_roninTokens[_i]][_chainIds[_i]].tokenAddr = _mainchainTokens[_i];
_mainchainToken[_roninTokens[_i]][_chainIds[_i]].erc = _standards[_i];

Expand All @@ -363,27 +357,30 @@ contract RoninGatewayV3 is
uint256 id = receipt.id;
receipt.info.validate();
if (receipt.kind != Transfer.Kind.Deposit) revert ErrInvalidReceiptKind();

if (receipt.ronin.chainId != block.chainid) revert ErrInvalidChainId(msg.sig, receipt.ronin.chainId, block.chainid);

MappedToken memory _token = getMainchainToken(receipt.ronin.tokenAddr, receipt.mainchain.chainId);
MappedToken memory token = getMainchainToken(receipt.ronin.tokenAddr, receipt.mainchain.chainId);

if (!(_token.erc == receipt.info.erc && _token.tokenAddr == receipt.mainchain.tokenAddr))
if (!(token.erc == receipt.info.erc && token.tokenAddr == receipt.mainchain.tokenAddr)) {
revert ErrInvalidReceipt();
}

IsolatedGovernance.Vote storage _proposal = depositVote[receipt.mainchain.chainId][id];
bytes32 _receiptHash = receipt.hash();
VoteStatus _status = _castIsolatedVote(_proposal, operator, minVoteWeight, _receiptHash);
VoteStatus status = _castIsolatedVote(_proposal, operator, minVoteWeight, _receiptHash);
emit DepositVoted(operator, id, receipt.mainchain.chainId, _receiptHash);
if (_status == VoteStatus.Approved) {

// Transfer assets and handle when the vote is approved.
IBridgeTracking bridgeTrackingContract = IBridgeTracking(getContract(ContractType.BRIDGE_TRACKING));
if (status == VoteStatus.Approved) {
_proposal.status = VoteStatus.Executed;
receipt.info.handleAssetTransfer(payable(receipt.ronin.addr), receipt.ronin.tokenAddr, IWETH(address(0)));
IBridgeTracking(getContract(ContractType.BRIDGE_TRACKING)).handleVoteApproved(
IBridgeTracking.VoteKind.Deposit,
receipt.id
);
bridgeTrackingContract.handleVoteApproved(IBridgeTracking.VoteKind.Deposit, receipt.id);
emit Deposited(_receiptHash, receipt);
}

// Announce to BridgeTracking to record the vote, after marking the VoteStatus as Executed.
bridgeTrackingContract.recordVote(IBridgeTracking.VoteKind.Deposit, receipt.id, operator);
}

/**
Expand Down Expand Up @@ -418,12 +415,8 @@ contract RoninGatewayV3 is
address _mainchainTokenAddr
) internal returns (uint256 _withdrawalId) {
_withdrawalId = withdrawalCount++;
Transfer.Receipt memory _receipt = _request.into_withdrawal_receipt(
_requester,
_withdrawalId,
_mainchainTokenAddr,
_chainId
);
Transfer.Receipt memory _receipt =
_request.into_withdrawal_receipt(_requester, _withdrawalId, _mainchainTokenAddr, _chainId);
withdrawal[_withdrawalId] = _receipt;
emit WithdrawalRequested(_receipt.hash(), _receipt);
}
Expand Down Expand Up @@ -468,9 +461,8 @@ contract RoninGatewayV3 is
IsolatedGovernance.Vote storage _v,
bytes32 _hash
) internal view returns (uint256 _totalWeight) {
(, address[] memory bridgeOperators, uint96[] memory weights) = IBridgeManager(
getContract(ContractType.BRIDGE_MANAGER)
).getFullBridgeOperatorInfos();
(, address[] memory bridgeOperators, uint96[] memory weights) =
IBridgeManager(getContract(ContractType.BRIDGE_MANAGER)).getFullBridgeOperatorInfos();
uint256 length = bridgeOperators.length;
unchecked {
for (uint _i; _i < length; ++_i) {
Expand Down Expand Up @@ -513,11 +505,7 @@ contract RoninGatewayV3 is
_trustedDenom = _trustedDenominator;
unchecked {
emit TrustedThresholdUpdated(
nonce++,
_trustedNumerator,
_trustedDenominator,
_previousTrustedNum,
_previousTrustedDenom
nonce++, _trustedNumerator, _trustedDenominator, _previousTrustedNum, _previousTrustedDenom
);
}
}
Expand Down
30 changes: 30 additions & 0 deletions test/bridge/integration/BaseIntegration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -582,4 +582,34 @@ contract BaseIntegration_Test is Base_Test {
vm.warp(nextDayTimestamp);
vm.roll(epochEndingBlockNumber);
}

function logBridgeTracking() public view {
console.log(">> logBridgeTracking ....");
uint256 currentPeriod = _validatorSet.currentPeriod();
uint256 lastSyncedPeriod = uint256(vm.load(address(_bridgeTracking), bytes32(uint256(11))));
console.log(" -> current period:", currentPeriod);
console.log(" -> total votes:", _bridgeTracking.totalVote(currentPeriod));
console.log(" -> total ballot:", _bridgeTracking.totalBallot(currentPeriod));
for (uint256 i; i < _param.roninBridgeManager.bridgeOperators.length; i++) {
address operator = _param.roninBridgeManager.bridgeOperators[i];
console.log(" -> total ballot of:", operator, _bridgeTracking.totalBallotOf(currentPeriod, operator));
}

console.log(" -> lastSynced period:", lastSyncedPeriod);
console.log(" -> total votes:", _bridgeTracking.totalVote(lastSyncedPeriod));
console.log(" -> total ballot:", _bridgeTracking.totalBallot(lastSyncedPeriod));
for (uint256 i; i < _param.roninBridgeManager.bridgeOperators.length; i++) {
address operator = _param.roninBridgeManager.bridgeOperators[i];
console.log(" -> total ballot of:", operator, _bridgeTracking.totalBallotOf(lastSyncedPeriod, operator));
}
}

function logBridgeSlash() public view {
console.log(">> logBridgeSlash ....");

uint256[] memory periods = _bridgeSlash.getSlashUntilPeriodOf(_param.roninBridgeManager.bridgeOperators);
for (uint256 i; i < _param.roninBridgeManager.bridgeOperators.length; i++) {
console.log(" -> slash operator until:", _param.roninBridgeManager.bridgeOperators[i], periods[i]);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import { console2 as console } from "forge-std/console2.sol";
import { Transfer } from "@ronin/contracts/libraries/Transfer.sol";
import { Token } from "@ronin/contracts/libraries/Token.sol";
import "../../BaseIntegration.t.sol";

contract BulkDepositAndRecord_Gateway_Test is BaseIntegration_Test {
using Transfer for Transfer.Receipt;

address _newBridgeOperator;
uint256 _numOperatorsForVoteExecuted;
Transfer.Receipt _sampleReceipt;
Transfer.Receipt[] _bulkReceipts;
uint256 _id = 0;

function setUp() public virtual override {
super.setUp();

vm.deal(address(_bridgeReward), 10 ether);
_sampleReceipt = Transfer.Receipt({
id: 0,
kind: Transfer.Kind.Deposit,
ronin: Token.Owner({ addr: makeAddr("recipient"), tokenAddr: address(_roninWeth), chainId: block.chainid }),
mainchain: Token.Owner({ addr: makeAddr("requester"), tokenAddr: address(_mainchainWeth), chainId: block.chainid }),
info: Token.Info({ erc: Token.Standard.ERC20, id: 0, quantity: 100 })
});

_numOperatorsForVoteExecuted =
_param.roninBridgeManager.bridgeOperators.length * _param.roninBridgeManager.num / _param.roninBridgeManager.denom;
}

function test_bulkDepositFor_wrapUp_checkRewardAndSlash() public {
_depositFor();
_moveToEndPeriodAndWrapUpEpoch();

console.log("=============== First 50 Receipts ===========");
_bulkDepositFor();

_wrapUpEpoch();
_wrapUpEpoch();

_moveToEndPeriodAndWrapUpEpoch();

console.log("=============== Check slash and reward behavior ===========");
console.log("==== Check total ballot before new deposit ====");

logBridgeTracking();

uint256 lastSyncedPeriod = uint256(vm.load(address(_bridgeTracking), bytes32(uint256(11))));
for (uint256 i; i < _numOperatorsForVoteExecuted; i++) {
address operator = _param.roninBridgeManager.bridgeOperators[i];
assertEq(_bridgeTracking.totalBallotOf(lastSyncedPeriod, operator), _id);
}

for (uint256 i = _numOperatorsForVoteExecuted; i < _param.roninBridgeManager.bridgeOperators.length; i++) {
address operator = _param.roninBridgeManager.bridgeOperators[i];
assertEq(_bridgeTracking.totalBallotOf(lastSyncedPeriod, operator), 0);
}

console.log("==== Check total ballot after new deposit ====");
_depositFor();

logBridgeTracking();
logBridgeSlash();

lastSyncedPeriod = uint256(vm.load(address(_bridgeTracking), bytes32(uint256(11))));
for (uint256 i; i < _param.roninBridgeManager.bridgeOperators.length; i++) {
address operator = _param.roninBridgeManager.bridgeOperators[i];
assertEq(_bridgeTracking.totalBallotOf(lastSyncedPeriod, operator), 0);
}

uint256[] memory toPeriodSlashArr = _bridgeSlash.getSlashUntilPeriodOf(_param.roninBridgeManager.bridgeOperators);
for (uint256 i = _numOperatorsForVoteExecuted; i < _param.roninBridgeManager.bridgeOperators.length; i++) {
assertEq(toPeriodSlashArr[i], 7);
}
}

function _depositFor() internal {
console.log(">> depositFor ....");
_sampleReceipt.id = ++_id;
for (uint256 i; i < _numOperatorsForVoteExecuted; i++) {
console.log(" -> Operator vote:", _param.roninBridgeManager.bridgeOperators[i]);
vm.prank(_param.roninBridgeManager.bridgeOperators[i]);
_roninGatewayV3.depositFor(_sampleReceipt);
}
}

function _bulkDepositFor() internal {
console.log(">> bulkDepositFor ....");
_prepareBulkRequest();
for (uint256 i; i < _numOperatorsForVoteExecuted; i++) {
console.log(" -> Operator vote:", _param.roninBridgeManager.bridgeOperators[i]);
vm.prank(_param.roninBridgeManager.bridgeOperators[i]);
_roninGatewayV3.tryBulkDepositFor(_bulkReceipts);
}
}

function _prepareBulkRequest() internal {
delete _bulkReceipts;

for (uint256 i; i < 50; i++) {
_sampleReceipt.id = ++_id;
_bulkReceipts.push(_sampleReceipt);
}
}
}
Loading

0 comments on commit 1be539e

Please sign in to comment.