Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: handle vote on bulk deposit for #2

Merged
merged 4 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
Comment on lines +193 to +197
Copy link
Collaborator

@huyhuynh3103 huyhuynh3103 Mar 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_executedReceipts = new bool[](receipts.length);
uint256 minVoteWeight = minimumVoteWeight();
Transfer.Receipt memory iReceipt;
for (uint i; i < receipts.length; ++i) {
uint256 length = receipts.length;
_executedReceipts = new bool[](length);
uint256 minVoteWeight = minimumVoteWeight();
Transfer.Receipt memory iReceipt;
for (uint i; i < 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