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

feat: storage mirror root registry #10

Merged
merged 4 commits into from
Nov 21, 2023
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
69 changes: 69 additions & 0 deletions solidity/contracts/StorageMirrorRootRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.19;

import {IBlockHeaderOracle} from 'interfaces/IBlockHeaderOracle.sol';
import {IVerifierModule} from 'interfaces/IVerifierModule.sol';
import {IStorageMirrorRootRegistry} from 'interfaces/IStorageMirrorRootRegistry.sol';

/**
* @title StorageMirrorRootRegistry
* @notice This contract should accept and store storageRoots of the StorageMirror contract in L1.
*/
contract StorageMirrorRootRegistry is IStorageMirrorRootRegistry {
/**
* @notice The address of the StorageMirror contract in Home chain
*/
address public immutable STORAGE_MIRROR;

/**
* @notice The address of the Verifier Module
*/
IVerifierModule public immutable VERIFIER_MODULE;

/**
* @notice The block header oracle
*/
IBlockHeaderOracle public immutable BLOCK_HEADER_ORACLE;

/**
* @notice The latest verified storage root of the StorageMirror contract in Home chain
*/
bytes32 public latestVerifiedStorageMirrorStorageRoot;

/**
* @notice The latest verified block number of the Home chain
*/
uint256 public latestVerifiedBlockNumber;

constructor(address _storageMirror, IVerifierModule _verifierModule, IBlockHeaderOracle _blockHeaderOracle) {
STORAGE_MIRROR = _storageMirror;
VERIFIER_MODULE = _verifierModule;
BLOCK_HEADER_ORACLE = _blockHeaderOracle;
}

/**
* @notice Users can use to propose and verify a storage root of the StorageMirror contract in Home chain
* @dev Calls queryL1BlockHeader to get the block header of the Home chain
* @dev Call verifier module for the actual verificationn
* @param _accountProof The account proof of the StorageMirror contract in Home chain
*/
function proposeAndVerifyStorageMirrorStorageRoot(bytes memory _accountProof) external {
bytes memory _blockHeader = _queryL1BlockHeader();
excaliborr marked this conversation as resolved.
Show resolved Hide resolved

(bytes32 _latestVerifiedStorageMirrorStorageRoot, uint256 _blockNumber) =
VERIFIER_MODULE.extractStorageMirrorStorageRoot(_blockHeader, _accountProof);

latestVerifiedStorageMirrorStorageRoot = _latestVerifiedStorageMirrorStorageRoot;
latestVerifiedBlockNumber = _blockNumber;

Copy link
Contributor

Choose a reason for hiding this comment

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

After thinking more about it, i thinks its good UX to save the block number as well so a new searcher who wasnt previously subscribed to the events knows what the latest verified block was easily, it makes the operation slightly more expensive but i think its good UX for searchers,everything else looks good.

emit VerifiedStorageMirrorStorageRoot(_blockNumber, latestVerifiedStorageMirrorStorageRoot);
}

/**
* @notice Function that queries an oracle to get the latest bridged block header of the Home chain
* @return _blockHeader The block header of the Home chain
*/
function _queryL1BlockHeader() internal view returns (bytes memory _blockHeader) {
(_blockHeader,) = BLOCK_HEADER_ORACLE.getLatestBlockHeader();
}
}
2 changes: 1 addition & 1 deletion solidity/contracts/VerifierModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ contract VerifierModule is IVerifierModule {
IStorageMirror.SafeSettings memory _proposedSettings,
bytes memory _storageMirrorStorageProof
) internal view virtual returns (bytes32 _hashedProposedSettings) {
bytes32 _latestStorageRoot = STORAGE_MIRROR_ROOT_REGISTRY.latestVerifiedStorageRoot();
bytes32 _latestStorageRoot = STORAGE_MIRROR_ROOT_REGISTRY.latestVerifiedStorageMirrorStorageRoot();

// The slot of where the latest settings hash is stored in the storage mirror
bytes32 _safeSettingsSlot = keccak256(abi.encode(_safe, _LATEST_VERIFIED_SETTINGS_SLOT));
Expand Down
46 changes: 44 additions & 2 deletions solidity/interfaces/IStorageMirrorRootRegistry.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,57 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.19;

import {IVerifierModule} from 'interfaces/IVerifierModule.sol';
import {IBlockHeaderOracle} from 'interfaces/IBlockHeaderOracle.sol';

interface IStorageMirrorRootRegistry {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/

/**
* @notice Emits after the storage root gets verified
* @param _homeChainBlockNumber The block number of the Home chain
* @param _storageRoot The storage root of the StorageMirror contract in Home chain that was verified
*/
event VerifiedStorageMirrorStorageRoot(uint256 indexed _homeChainBlockNumber, bytes32 _storageRoot);

/*///////////////////////////////////////////////////////////////
VARIABLES
//////////////////////////////////////////////////////////////*/

/**
* @notice The latest verified storage root
* @notice The address of the StorageMirror contract in Home chain
* @return _storageMirror The address of the StorageMirror contract in Home chain
*/
function STORAGE_MIRROR() external view returns (address _storageMirror);

/**
* @notice The address of the Verifier Module
* @return _verifierModule The address of the Verifier Module
*/
function VERIFIER_MODULE() external view returns (IVerifierModule _verifierModule);

/**
* @notice The address of the Block Header Oracle
* @return _blockHeaderOracle The address of the Block Header Oracle
*/
function BLOCK_HEADER_ORACLE() external view returns (IBlockHeaderOracle _blockHeaderOracle);

/**
* @notice The latest verified block number of the Home chain
* @return _latestVerifiedBlockNumber The latest verified block number of the Home chain
*/
function latestVerifiedBlockNumber() external view returns (uint256 _latestVerifiedBlockNumber);

/**
* @notice The latest verified storage root of the StorageMirror contract in Home chain
* @return _latestVerifiedStorageMirrorStorageRoot The latest verified storage root of the StorageMirror contract in Home chain
*/
function latestVerifiedStorageRoot() external view returns (bytes32 _latestVerifiedStorageRoot);
function latestVerifiedStorageMirrorStorageRoot()
external
view
returns (bytes32 _latestVerifiedStorageMirrorStorageRoot);

/*///////////////////////////////////////////////////////////////
LOGIC
Expand Down
88 changes: 88 additions & 0 deletions solidity/test/unit/StorageMirrorRootRegistry.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.4 <0.9.0;

import {Test} from 'forge-std/Test.sol';
import {BlockHeaderOracle} from 'contracts/BlockHeaderOracle.sol';
import {StorageMirrorRootRegistry} from 'contracts/StorageMirrorRootRegistry.sol';
import {IBlockHeaderOracle} from 'interfaces/IBlockHeaderOracle.sol';
import {IStorageMirrorRootRegistry} from 'interfaces/IStorageMirrorRootRegistry.sol';
import {IVerifierModule} from 'interfaces/IVerifierModule.sol';

contract StorageMirrorRootRegistryForTest is StorageMirrorRootRegistry {
constructor(
address _storageMirror,
IVerifierModule _verifierModule,
IBlockHeaderOracle _blockHeaderOracle
) StorageMirrorRootRegistry(_storageMirror, _verifierModule, _blockHeaderOracle) {}

function queryL1BlockHeader() external view returns (bytes memory _blockHeader) {
_blockHeader = _queryL1BlockHeader();
}
}

abstract contract Base is Test {
event VerifiedStorageMirrorStorageRoot(uint256 indexed _homeChainBlockNumber, bytes32 _storageRoot);

address public user;
address public storageMirror;
StorageMirrorRootRegistry public storageMirrorRootRegistry;
StorageMirrorRootRegistryForTest public storageMirrorRootRegistryForTest;
BlockHeaderOracle public blockHeaderOracle;
IVerifierModule public verifierModule;

function setUp() public {
user = makeAddr('user');
storageMirror = makeAddr('StorageMirror');
blockHeaderOracle = new BlockHeaderOracle();
verifierModule = IVerifierModule(makeAddr('VerifierModule'));
storageMirrorRootRegistry =
new StorageMirrorRootRegistry(storageMirror, verifierModule, IBlockHeaderOracle(blockHeaderOracle));
storageMirrorRootRegistryForTest =
new StorageMirrorRootRegistryForTest(storageMirror, verifierModule, IBlockHeaderOracle(blockHeaderOracle));
}
}

contract UnitStorageMirrorRootRegistryQueryL1BlockHeader is Base {
function testQueryL1BlockHeader(bytes memory _blockHeader, uint256 _blockTimestamp, uint256 _blockNumber) public {
vm.prank(user);
blockHeaderOracle.updateBlockHeader(_blockHeader, _blockTimestamp, _blockNumber);

vm.expectCall(address(blockHeaderOracle), abi.encodeWithSelector(blockHeaderOracle.getLatestBlockHeader.selector));
vm.prank(user);
bytes memory _savedBlockHeader = storageMirrorRootRegistryForTest.queryL1BlockHeader();

assertEq(_blockHeader, _savedBlockHeader, 'Block header should be saved');
}
}

contract UnitStorageMirrorRootRegistryProposeAndVerifyStorageMirrorStorageRoot is Base {
function testProposeAndVerifyStorageMirrorStorageRoot(bytes memory _accountProof) public {
bytes memory _blockHeader = '0x1234';
uint256 _blockTimestamp = 1234;
uint256 _blockNumber = 1234;
bytes32 _storageRoot = '0x1234';

vm.prank(user);
blockHeaderOracle.updateBlockHeader(_blockHeader, _blockTimestamp, _blockNumber);

vm.mockCall(
address(verifierModule),
abi.encodeWithSelector(verifierModule.extractStorageMirrorStorageRoot.selector, _blockHeader, _accountProof),
abi.encode(_storageRoot, _blockNumber)
);
vm.expectCall(
address(verifierModule),
abi.encodeWithSelector(verifierModule.extractStorageMirrorStorageRoot.selector, _blockHeader, _accountProof)
);

vm.expectEmit(true, true, true, true);
emit VerifiedStorageMirrorStorageRoot(_blockNumber, _storageRoot);

vm.prank(user);
storageMirrorRootRegistry.proposeAndVerifyStorageMirrorStorageRoot(_accountProof);

assertEq(
_storageRoot, storageMirrorRootRegistry.latestVerifiedStorageMirrorStorageRoot(), 'Storage root should be saved'
);
}
}
15 changes: 8 additions & 7 deletions solidity/test/unit/VerifierModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ contract TestVerifierModule is VerifierModule {
IStorageMirror.SafeSettings memory _proposedSettings,
bytes memory _storageMirrorStorageProof
) public view returns (bytes32 _hashedProposedSettings) {
bytes32 _latestStorageRoot = IStorageMirrorRootRegistry(STORAGE_MIRROR_ROOT_REGISTRY).latestVerifiedStorageRoot();
bytes32 _latestStorageRoot =
IStorageMirrorRootRegistry(STORAGE_MIRROR_ROOT_REGISTRY).latestVerifiedStorageMirrorStorageRoot();

// The slot of where the latest settings hash is stored in the storage mirror
bytes32 _safeSettingsSlot = keccak256(abi.encode(_safe, 0));
Expand Down Expand Up @@ -337,7 +338,7 @@ contract UnitMerklePatriciaTree is Base {

vm.mockCall(
address(_storageMirrorRegistry),
abi.encodeWithSelector(IStorageMirrorRootRegistry.latestVerifiedStorageRoot.selector),
abi.encodeWithSelector(IStorageMirrorRootRegistry.latestVerifiedStorageMirrorStorageRoot.selector),
abi.encode(_fakeStorageRoot)
);

Expand Down Expand Up @@ -380,7 +381,7 @@ contract UnitMerklePatriciaTree is Base {

vm.mockCall(
address(_storageMirrorRegistry),
abi.encodeWithSelector(IStorageMirrorRootRegistry.latestVerifiedStorageRoot.selector),
abi.encodeWithSelector(IStorageMirrorRootRegistry.latestVerifiedStorageMirrorStorageRoot.selector),
abi.encode(_fakeStorageRoot)
);

Expand Down Expand Up @@ -412,7 +413,7 @@ contract UnitMerklePatriciaTree is Base {

vm.mockCall(
address(_storageMirrorRegistry),
abi.encodeWithSelector(IStorageMirrorRootRegistry.latestVerifiedStorageRoot.selector),
abi.encodeWithSelector(IStorageMirrorRootRegistry.latestVerifiedStorageMirrorStorageRoot.selector),
abi.encode(_fakeStorageRoot)
);

Expand Down Expand Up @@ -461,7 +462,7 @@ contract UnitMerklePatriciaTree is Base {

vm.mockCall(
address(_storageMirrorRegistry),
abi.encodeWithSelector(IStorageMirrorRootRegistry.latestVerifiedStorageRoot.selector),
abi.encodeWithSelector(IStorageMirrorRootRegistry.latestVerifiedStorageMirrorStorageRoot.selector),
abi.encode(_fakeStorageRoot)
);

Expand Down Expand Up @@ -563,7 +564,7 @@ contract UnitMerklePatriciaTree is Base {

vm.mockCall(
address(_storageMirrorRegistry),
abi.encodeWithSelector(IStorageMirrorRootRegistry.latestVerifiedStorageRoot.selector),
abi.encodeWithSelector(IStorageMirrorRootRegistry.latestVerifiedStorageMirrorStorageRoot.selector),
abi.encode(_fakeStorageRoot)
);

Expand Down Expand Up @@ -655,7 +656,7 @@ contract UnitMerklePatriciaTree is Base {

vm.mockCall(
address(_storageMirrorRegistry),
abi.encodeWithSelector(IStorageMirrorRootRegistry.latestVerifiedStorageRoot.selector),
abi.encodeWithSelector(IStorageMirrorRootRegistry.latestVerifiedStorageMirrorStorageRoot.selector),
abi.encode(_fakeStorageRoot)
);

Expand Down