Skip to content

Commit a411309

Browse files
authored
Add L2 to L1 Transition Root functionality (#32)
* Updated version of @solarity/solidity-lib and added poseidon-solidity * Used poseidon-solidity for L2 optimizations * Added L2 to L1 Transition Root functionality * Emitted event in setRegistrationRoot * Fixed tests * Resolved comments after review * Added tests for L1RegistrationState and RegistrationSMT * Updated CHANGELOG and versions
1 parent 069e7b3 commit a411309

15 files changed

+648
-58
lines changed

CHANGELOG.md

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## [0.2.1]
4+
5+
* Added `L1RegistrationState` and `RegistrationSMT` contracts for automatic root transition from L2 Rarimo to L1.
6+
37
## [0.2.0]
48

59
* Removed TSS integration from all the contracts.
@@ -224,31 +228,3 @@
224228
* Fixed all the tests except ZK.
225229
* Fixed migration scripts. Added config resolution based on deployment chain.
226230
* Updated natspec.
227-
228-
## [Unreleased]
229-
230-
* Made the `PoseidonSMT` and `Registration` contracts upgradable via TSS.
231-
* Copied `UUPSSignableUpgradeable` from the `@rarimo/evm-bridge-contracts` package to the `Registration` and `PoseidonSMT` contracts to support upgradability via TSS.
232-
* Modified `UUPSSignableUpgradeable` to work with encoded (signature + MTP), not just signature
233-
* Rewrote tests and deployment scripts to deploy `ERC1967Proxy` before initializing the `Registration` and `PoseidonSMT` contracts.
234-
* Added the `_disableInitializers()` function in the constructors of the `Registration` and `PoseidonSMT` contracts.
235-
* Added the ability for the `PoseidonSMT` contract to handle multiple registrations.
236-
* Restricted adding or removing registrations to the TSS.
237-
* Replaced `OwnableUpgradeable` functionality in the registration contract with the functionality of TSS, so dispatchers can be added or removed only via signatures.
238-
* Added the ability to set `methodId` as the first key of the `_nonces` and `_usedNonces` fields to separate them for different methods within the contract.
239-
* Added a `nonces` mapping in the `TSSSigner` contract to have the functionality of getting them for calling specific methods within the contract, for example, for implementation upgrades via TSS.
240-
* Added the ability to set `chainName` in the `TSSSigner` contract.
241-
* The `chainName` field is used to add support for contract upgradability and other actions via TSS.
242-
* It was also added due to current conventions in the existing system.
243-
* Cleaned up tests.
244-
245-
### Added
246-
247-
- Add Noir passport verifiers:
248-
- Z_NOIR_PASSPORT_1_256_3_4_600_248_1_1496_3_256,
249-
- Z_NOIR_PASSPORT_2_256_3_6_248_336_1_2432_3_256,
250-
- Z_NOIR_PASSPORT_2_256_3_6_336_248_1_2432_3_256,
251-
- Z_NOIR_PASSPORT_2_256_3_6_336_264_21_2448_6_2008,
252-
- Z_NOIR_PASSPORT_10_256_3_3_576_248_1_1184_5_264,
253-
- Z_NOIR_PASSPORT_20_256_3_3_336_224_NA,
254-
- Z_NOIR_PASSPORT_21_256_3_3_576_232_NA.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.19;
3+
4+
/**
5+
* @title Interface declaring pre-existing cross-chain messaging functions, events and errors.
6+
* @author ConsenSys Software Inc.
7+
* @custom:security-contact security-report@linea.build
8+
*/
9+
interface IMessageService {
10+
/**
11+
* @notice Emitted when a message is sent.
12+
* @param _from The indexed sender address of the message (msg.sender).
13+
* @param _to The indexed intended recipient address of the message on the other layer.
14+
* @param _fee The fee being being paid to deliver the message to the recipient in Wei.
15+
* @param _value The value being sent to the recipient in Wei.
16+
* @param _nonce The unique message number.
17+
* @param _calldata The calldata being passed to the intended recipient when being called on claiming.
18+
* @param _messageHash The indexed hash of the message parameters.
19+
* @dev _calldata has the _ because calldata is a reserved word.
20+
* @dev We include the message hash to save hashing costs on the rollup.
21+
* @dev This event is used on both L1 and L2.
22+
*/
23+
event MessageSent(
24+
address indexed _from,
25+
address indexed _to,
26+
uint256 _fee,
27+
uint256 _value,
28+
uint256 _nonce,
29+
bytes _calldata,
30+
bytes32 indexed _messageHash
31+
);
32+
33+
/**
34+
* @notice Emitted when a message is claimed.
35+
* @param _messageHash The indexed hash of the message that was claimed.
36+
*/
37+
event MessageClaimed(bytes32 indexed _messageHash);
38+
39+
/**
40+
* @dev Thrown when fees are lower than the minimum fee.
41+
*/
42+
error FeeTooLow();
43+
44+
/**
45+
* @dev Thrown when the value sent is less than the fee.
46+
* @dev Value to forward on is msg.value - _fee.
47+
*/
48+
error ValueSentTooLow();
49+
50+
/**
51+
* @dev Thrown when the destination address reverts.
52+
*/
53+
error MessageSendingFailed(address destination);
54+
55+
/**
56+
* @dev Thrown when the recipient address reverts.
57+
*/
58+
error FeePaymentFailed(address recipient);
59+
60+
/**
61+
* @notice Sends a message for transporting from the given chain.
62+
* @dev This function should be called with a msg.value = _value + _fee. The fee will be paid on the destination chain.
63+
* @param _to The destination address on the destination chain.
64+
* @param _fee The message service fee on the origin chain.
65+
* @param _calldata The calldata used by the destination message service to call the destination contract.
66+
*/
67+
function sendMessage(address _to, uint256 _fee, bytes calldata _calldata) external payable;
68+
69+
/**
70+
* @notice Deliver a message to the destination chain.
71+
* @notice Is called by the Postman, dApp or end user.
72+
* @param _from The msg.sender calling the origin message service.
73+
* @param _to The destination address on the destination chain.
74+
* @param _value The value to be transferred to the destination address.
75+
* @param _fee The message service fee on the origin chain.
76+
* @param _feeRecipient Address that will receive the fees.
77+
* @param _calldata The calldata used by the destination message service to call/forward to the destination contract.
78+
* @param _nonce Unique message number.
79+
*/
80+
function claimMessage(
81+
address _from,
82+
address _to,
83+
uint256 _fee,
84+
uint256 _value,
85+
address payable _feeRecipient,
86+
bytes calldata _calldata,
87+
uint256 _nonce
88+
) external;
89+
90+
/**
91+
* @notice Returns the original sender of the message on the origin layer.
92+
* @return originalSender The original sender of the message on the origin layer.
93+
*/
94+
function sender() external view returns (address originalSender);
95+
}

contracts/libraries/Poseidon.sol

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.21;
33

4-
// solhint-disable
4+
import {PoseidonT2} from "poseidon-solidity/PoseidonT2.sol";
5+
import {PoseidonT3} from "poseidon-solidity/PoseidonT3.sol";
6+
import {PoseidonT4} from "poseidon-solidity/PoseidonT4.sol";
7+
import {PoseidonT5} from "poseidon-solidity/PoseidonT5.sol";
8+
import {PoseidonT6} from "poseidon-solidity/PoseidonT6.sol";
59

610
library PoseidonUnit1L {
7-
function poseidon(uint256[1] calldata) public pure returns (uint256) {}
11+
function poseidon(uint256[1] calldata inputs_) public pure returns (uint256) {
12+
return PoseidonT2.hash([inputs_[0]]);
13+
}
814
}
915

1016
library PoseidonUnit2L {
11-
function poseidon(uint256[2] calldata) public pure returns (uint256) {}
17+
function poseidon(uint256[2] calldata inputs_) public pure returns (uint256) {
18+
return PoseidonT3.hash([inputs_[0], inputs_[1]]);
19+
}
1220
}
1321

1422
library PoseidonUnit3L {
15-
function poseidon(uint256[3] calldata) public pure returns (uint256) {}
23+
function poseidon(uint256[3] calldata inputs_) public pure returns (uint256) {
24+
return PoseidonT4.hash([inputs_[0], inputs_[1], inputs_[2]]);
25+
}
1626
}
1727

1828
library PoseidonUnit4L {
19-
function poseidon(uint256[4] calldata) public pure returns (uint256) {}
29+
function poseidon(uint256[4] calldata inputs_) public pure returns (uint256) {
30+
return PoseidonT5.hash([inputs_[0], inputs_[1], inputs_[2], inputs_[3]]);
31+
}
2032
}
2133

2234
library PoseidonUnit5L {
23-
function poseidon(uint256[5] calldata) public pure returns (uint256) {}
35+
function poseidon(uint256[5] calldata inputs_) public pure returns (uint256) {
36+
return PoseidonT6.hash([inputs_[0], inputs_[1], inputs_[2], inputs_[3], inputs_[4]]);
37+
}
2438
}

contracts/mock/MessageServiceMock.sol

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.21;
3+
4+
contract MessageServiceMock {
5+
function sendMessage(address, uint256, bytes memory) external {
6+
// Mock implementation
7+
}
8+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.21;
3+
4+
import {RegistrationSMT} from "../../state/RegistrationSMT.sol";
5+
6+
contract RegistrationSMTMock is RegistrationSMT {
7+
function mockRoot(bytes32 newRoot_) external {
8+
_roots[newRoot_] = block.timestamp;
9+
}
10+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.21;
3+
4+
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
5+
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
6+
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
7+
8+
import {TypeCaster} from "@solarity/solidity-lib/libs/utils/TypeCaster.sol";
9+
import {AMultiOwnable} from "@solarity/solidity-lib/access/AMultiOwnable.sol";
10+
11+
contract L1RegistrationState is Initializable, AMultiOwnable, UUPSUpgradeable {
12+
using TypeCaster for address;
13+
14+
uint256 public constant ROOT_VALIDITY = 1 hours;
15+
16+
address public rarimoRollup;
17+
18+
bytes32 public latestRoot;
19+
uint256 public latestRootTimestamp;
20+
21+
mapping(bytes32 => uint256) public roots;
22+
23+
event RootSet(bytes32 root);
24+
25+
error NotRarimoRollup(address sender);
26+
27+
modifier onlyRarimoRollup() {
28+
_requireRarimoRollup();
29+
_;
30+
}
31+
32+
constructor() {
33+
_disableInitializers();
34+
}
35+
36+
function __L1RegistrationState_init(
37+
address initialOwner_,
38+
address rarimoRollup_
39+
) external initializer {
40+
__AMultiOwnable_init(initialOwner_.asSingletonArray());
41+
42+
rarimoRollup = rarimoRollup_;
43+
}
44+
45+
function setRegistrationRoot(bytes32 root_, uint256 timestamp_) external onlyRarimoRollup {
46+
roots[root_] = timestamp_;
47+
48+
if (timestamp_ >= latestRootTimestamp) {
49+
latestRoot = root_;
50+
latestRootTimestamp = timestamp_;
51+
}
52+
53+
emit RootSet(root_);
54+
}
55+
56+
function setRarimoRollup(address rarimoRollup_) external onlyOwner {
57+
rarimoRollup = rarimoRollup_;
58+
}
59+
60+
function isRootLatest(bytes32 root_) public view virtual returns (bool) {
61+
return latestRoot == root_;
62+
}
63+
64+
function isRootValid(bytes32 root_) external view virtual returns (bool) {
65+
if (root_ == bytes32(0)) {
66+
return false;
67+
}
68+
69+
return isRootLatest(root_) || roots[root_] + ROOT_VALIDITY > block.timestamp;
70+
}
71+
72+
// solhint-disable-next-line no-empty-blocks
73+
function _authorizeUpgrade(address) internal virtual override onlyOwner {}
74+
75+
function implementation() external view returns (address) {
76+
return ERC1967Utils.getImplementation();
77+
}
78+
79+
function _requireRarimoRollup() internal view {
80+
if (msg.sender != rarimoRollup) {
81+
revert NotRarimoRollup(msg.sender);
82+
}
83+
}
84+
}

contracts/state/PoseidonSMT.sol

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ contract PoseidonSMT is Initializable, UUPSUpgradeable {
3838
_commitRoot();
3939
}
4040

41+
modifier onlyOwner() {
42+
_onlyOwner();
43+
_;
44+
}
45+
4146
constructor() {
4247
_disableInitializers();
4348
}
@@ -126,7 +131,7 @@ contract PoseidonSMT is Initializable, UUPSUpgradeable {
126131
_roots[_bytes32Tree.getRoot()] = block.timestamp;
127132
}
128133

129-
function _commitRoot() internal {
134+
function _commitRoot() internal virtual {
130135
bytes32 root_ = _bytes32Tree.getRoot();
131136

132137
IEvidenceRegistry(evidenceRegistry).addStatement(root_, bytes32(block.timestamp));
@@ -159,9 +164,7 @@ contract PoseidonSMT is Initializable, UUPSUpgradeable {
159164
require(StateKeeper(stateKeeper).isOwner(msg.sender), "PoseidonSMT: not an owner");
160165
}
161166

162-
function _authorizeUpgrade(address) internal virtual override {
163-
_onlyOwner();
164-
}
167+
function _authorizeUpgrade(address) internal virtual override onlyOwner {}
165168

166169
function implementation() external view returns (address) {
167170
return ERC1967Utils.getImplementation();

contracts/state/RegistrationSMT.sol

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.21;
3+
4+
import {SparseMerkleTree} from "@solarity/solidity-lib/libs/data-structures/SparseMerkleTree.sol";
5+
6+
import {PoseidonSMT} from "./PoseidonSMT.sol";
7+
import {L1RegistrationState} from "./L1RegistrationState.sol";
8+
9+
import {IMessageService} from "../interfaces/rollup/IMessageService.sol";
10+
11+
contract RegistrationSMT is PoseidonSMT {
12+
using SparseMerkleTree for SparseMerkleTree.Bytes32SMT;
13+
14+
address public l2MessageService;
15+
address public l1RegistrationState;
16+
17+
function __SetL1TransitionRootData_init(
18+
address l2MessageService_,
19+
address l1RegistrationState_
20+
) external reinitializer(2) {
21+
l2MessageService = l2MessageService_;
22+
l1RegistrationState = l1RegistrationState_;
23+
}
24+
25+
function _commitRoot() internal override {
26+
bytes32 root_ = _bytes32Tree.getRoot();
27+
28+
IMessageService(l2MessageService).sendMessage(
29+
l1RegistrationState,
30+
0,
31+
abi.encodeWithSelector(
32+
L1RegistrationState.setRegistrationRoot.selector,
33+
root_,
34+
block.timestamp
35+
)
36+
);
37+
38+
super._commitRoot();
39+
}
40+
41+
function setL1RegistrationState(address l1RegistrationState_) external onlyOwner {
42+
l1RegistrationState = l1RegistrationState_;
43+
}
44+
45+
function setL2MessageService(address l2MessageService_) external onlyOwner {
46+
l2MessageService = l2MessageService_;
47+
}
48+
}

0 commit comments

Comments
 (0)