Skip to content

Commit

Permalink
add syncRangeBatchRoot
Browse files Browse the repository at this point in the history
  • Loading branch information
zkbenny committed Apr 1, 2024
1 parent bc5c68b commit e6f96ba
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 19 deletions.
49 changes: 31 additions & 18 deletions contracts/Arbitrator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {DoubleEndedQueueUpgradeable} from "@openzeppelin/contracts-upgradeable/u
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IArbitrator} from "./interfaces/IArbitrator.sol";
import {IL1Gateway} from "./interfaces/IL1Gateway.sol";
import {IZkLink} from "./interfaces/IZkLink.sol";
import {IAdmin} from "./zksync/l1-contracts/zksync/interfaces/IAdmin.sol";
import {IZkSync} from "./zksync/l1-contracts/zksync/interfaces/IZkSync.sol";
import {FeeParams} from "./zksync/l1-contracts/zksync/Storage.sol";
Expand All @@ -18,6 +19,11 @@ import {FeeParams} from "./zksync/l1-contracts/zksync/Storage.sol";
contract Arbitrator is IArbitrator, OwnableUpgradeable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
using DoubleEndedQueueUpgradeable for DoubleEndedQueueUpgradeable.Bytes32Deque;

struct GatewayAdapterParams {
IL1Gateway gateway;
bytes adapterParams;
}

/// @dev The gateway for sending message from ethereum to primary chain
IL1Gateway public primaryChainGateway;
/// @dev The gateway for sending message from ethereum to secondary chain
Expand All @@ -28,16 +34,14 @@ contract Arbitrator is IArbitrator, OwnableUpgradeable, UUPSUpgradeable, Reentra
mapping(IL1Gateway => DoubleEndedQueueUpgradeable.Bytes32Deque) public secondaryChainMessageHashQueues;
/// @notice List of permitted relayers
mapping(address relayerAddress => bool isRelayer) public relayers;
/// @dev The msg value and adapter params are used to forward a l2 message from source chain to target chain
bool private claiming;
uint256 private claimMsgValue;
bytes private claimAdapterParams;
/// @dev The forward params are used to forward a l2 message from source chain to target chains
bytes private forwardParams;
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[47] private __gap;
uint256[49] private __gap;

/// @notice Primary chain gateway init
event InitPrimaryChain(IL1Gateway indexed gateway);
Expand Down Expand Up @@ -142,22 +146,33 @@ contract Arbitrator is IArbitrator, OwnableUpgradeable, UUPSUpgradeable, Reentra

/// @dev This function is called within the `claimMessageCallback` of L1 gateway
function receiveMessage(uint256 _value, bytes calldata _callData) external payable {
// `claiming`, `claimMsgValue` and `claimAdapterParams` are transient values and set in `claimMessage`
// Ensure claim start from call `claimMessage`
require(claiming, "Invalid claim");
require(msg.value == _value, "Invalid msg value");
IL1Gateway gateway = IL1Gateway(msg.sender);
// Ensure the caller is L1 gateway
if (gateway == primaryChainGateway) {
// Unpack destination chain and final callData
(IL1Gateway secondaryChainGateway, bytes memory finalCallData) = abi.decode(_callData, (IL1Gateway, bytes));
require(secondaryChainGateways[secondaryChainGateway], "Invalid secondary chain gateway");
// Forward fee to send message
secondaryChainGateway.sendMessage{value: claimMsgValue + _value}(_value, finalCallData, claimAdapterParams);
bytes[] memory gatewayCallDataLists = abi.decode(_callData, (bytes[]));
// `forwardParams` is set in `claimMessage`
bytes[] memory gatewayForwardParams = abi.decode(forwardParams, (bytes[]));
uint256 gatewayLength = gatewayCallDataLists.length;
require(gatewayLength == gatewayForwardParams.length, "Invalid forward params length");
unchecked {
for (uint256 i = 0; i < gatewayLength; ++i) {
bytes memory gatewayCallData = gatewayCallDataLists[i];
bytes memory gatewayForwardParam = gatewayForwardParams[i];
(IL1Gateway secondaryChainGateway, uint256 callValue, bytes memory callData) = abi.decode(gatewayCallData, (IL1Gateway, uint256, bytes));
require(secondaryChainGateways[secondaryChainGateway], "Invalid secondary chain gateway");
(uint256 sendMsgFee, bytes memory adapterParams) = abi.decode(gatewayForwardParam, (uint256, bytes));
// Forward fee to send message
secondaryChainGateway.sendMessage{value: sendMsgFee + callValue}(callValue, callData, adapterParams);
}
}
} else {
require(secondaryChainGateways[gateway], "Not secondary chain gateway");
// `forwardParams` is set in `claimMessage`
(uint256 sendMsgFee, bytes memory adapterParams) = abi.decode(forwardParams, (uint256, bytes));
// Forward fee to send message
primaryChainGateway.sendMessage{value: claimMsgValue + _value}(_value, _callData, claimAdapterParams);
primaryChainGateway.sendMessage{value: sendMsgFee + _value}(_value, _callData, adapterParams);
}
emit MessageForwarded(gateway, _value, _callData);
}
Expand Down Expand Up @@ -191,13 +206,11 @@ contract Arbitrator is IArbitrator, OwnableUpgradeable, UUPSUpgradeable, Reentra
function claimMessage(
address _sourceChainCanonicalMessageService,
bytes calldata _sourceChainClaimCallData,
bytes memory _targetChainAdapterParams
bytes memory _forwardParams
) external payable nonReentrant onlyRelayer {
// The `claiming`, `claimMsgValue` and `claimAdapterParams` will be cleared after tx executed
// The `forwardParams` will be cleared after tx executed
assembly {
tstore(claiming.slot, true)
tstore(claimMsgValue.slot, callvalue())
tstore(claimAdapterParams.slot, _targetChainAdapterParams)
tstore(forwardParams.slot, _forwardParams)
}
// Call the claim interface of source chain message service
// And it will inner call the `claimCallback` interface of source chain L1Gateway
Expand Down
39 changes: 38 additions & 1 deletion contracts/ZkLink.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,15 @@ contract ZkLink is
public isEthWithdrawalFinalized;
/// @dev The forward fee allocator
address public forwardFeeAllocator;
/// @dev The range root hash of [fromBatchNumber, toBatchNumber]
/// The range = keccak256(abi.encodePacked(fromBatchNumber, toBatchNumber))
mapping(bytes32 range => bytes32 rangeRootHash) public rangRootHashMap;
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
uint256[49] private __gap;

/// @notice Gateway init
event InitGateway(IL2Gateway indexed gateway);
Expand All @@ -107,6 +110,8 @@ contract ZkLink is
event SyncL2Requests(uint256 totalSyncedPriorityTxs, bytes32 syncHash, uint256 forwardEthAmount);
/// @notice Emitted when receive batch root from primary chain.
event SyncBatchRoot(uint256 batchNumber, bytes32 l2LogsRootHash, uint256 forwardEthAmount);
/// @notice Emitted when receive range batch root from primary chain.
event SyncRangeBatchRoot(uint256 fromBatchNumber, uint256 toBatchNumber, bytes32 rangeRootHash, uint256 forwardEthAmount);
/// @notice Emitted when receive l2 tx hash from primary chain.
event SyncL2TxHash(bytes32 l2TxHash, bytes32 primaryChainL2TxHash);
/// @notice Emitted when validator withdraw forward fee
Expand Down Expand Up @@ -458,6 +463,38 @@ contract ZkLink is
emit SyncBatchRoot(_batchNumber, _l2LogsRootHash, _forwardEthAmount);
}

function syncRangeBatchRoot(
uint256 _fromBatchNumber, uint256 _toBatchNumber, bytes32 _rangeRootHash, uint256 _forwardEthAmount
) external payable onlyGateway {
require(_toBatchNumber >= _fromBatchNumber, "Invalid range");
require(msg.value == _forwardEthAmount, "Invalid forward amount");
bytes32 range = keccak256(abi.encodePacked(_fromBatchNumber, _toBatchNumber));
rangRootHashMap[range] = _rangeRootHash;
emit SyncRangeBatchRoot(_fromBatchNumber, _toBatchNumber, _rangeRootHash, _forwardEthAmount);
}

function openRangeBatchRoot(uint256 _fromBatchNumber, uint256 _toBatchNumber, bytes32[] memory _l2LogsRootHashes) external onlyValidator {
require(_toBatchNumber >= _fromBatchNumber, "Invalid range");
bytes32 range = keccak256(abi.encodePacked(_fromBatchNumber, _toBatchNumber));
bytes32 rangeRootHash = rangRootHashMap[range];
require(rangeRootHash != bytes32(0), "Rang root hash not exist");
uint256 rootHashLength = _l2LogsRootHashes.length;
require(rootHashLength == _toBatchNumber - _fromBatchNumber + 1, "Invalid root hashes length");
bytes32 openRangeRootHash = _l2LogsRootHashes[0];
l2LogsRootHashes[_fromBatchNumber] = openRangeRootHash;
unchecked {
for (uint256 i = 1; i < rootHashLength; ++i) {
bytes32 l2LogsRootHash = _l2LogsRootHashes[i];
l2LogsRootHashes[_fromBatchNumber + i] = l2LogsRootHash;
openRangeRootHash = Merkle._efficientHash(openRangeRootHash, l2LogsRootHash);
}
}
require(openRangeRootHash == rangeRootHash, "Incorrect range root hash");
if (_toBatchNumber > totalBatchesExecuted) {
totalBatchesExecuted = _toBatchNumber;
}
}

function syncL2TxHash(bytes32 _l2TxHash, bytes32 _primaryChainL2TxHash) external onlyGateway {
l2TxHashMap[_l2TxHash] = _primaryChainL2TxHash;
emit SyncL2TxHash(_l2TxHash, _primaryChainL2TxHash);
Expand Down
6 changes: 6 additions & 0 deletions contracts/interfaces/IZkLink.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ interface IZkLink {
/// @param _forwardEthAmount The forward eth amount
function syncBatchRoot(uint256 _batchNumber, bytes32 _l2LogsRootHash, uint256 _forwardEthAmount) external payable;

/// @notice Receive range batch root from primary chain
/// @param _fromBatchNumber The batch number from
/// @param _toBatchNumber The batch number to
/// @param _rangeRootHash The range root hash
function syncRangeBatchRoot(uint256 _fromBatchNumber, uint256 _toBatchNumber, bytes32 _rangeRootHash) external;

/// @notice Receive l2 tx hash from primary chain
/// @param _l2TxHash The l2 tx hash on local chain
/// @param _primaryChainL2TxHash The l2 tx hash on primary chain
Expand Down

0 comments on commit e6f96ba

Please sign in to comment.