-
Notifications
You must be signed in to change notification settings - Fork 310
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: Describe the new message box model (#4485)
Fixes #4250 and writes out an initial implementation of a solidity frontier tree.
- Loading branch information
Showing
15 changed files
with
600 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright 2023 Aztec Labs. | ||
pragma solidity >=0.8.18; | ||
|
||
interface IFrontier { | ||
function insertLeaf(bytes32 _leaf) external; | ||
|
||
function root() external view returns (bytes32); | ||
} |
85 changes: 85 additions & 0 deletions
85
l1-contracts/src/core/messagebridge/frontier_tree/Frontier.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright 2023 Aztec Labs. | ||
pragma solidity >=0.8.18; | ||
|
||
import {IFrontier} from "../../interfaces/messagebridge/IFrontier.sol"; | ||
|
||
contract FrontierMerkle is IFrontier { | ||
uint256 public immutable DEPTH; | ||
uint256 public immutable SIZE; | ||
|
||
uint256 internal nextIndex = 0; | ||
|
||
mapping(uint256 level => bytes32 node) public frontier; | ||
|
||
// Below can be pre-computed so it would be possible to have constants | ||
// for the zeros at each level. This would save gas on computations | ||
mapping(uint256 level => bytes32 zero) public zeros; | ||
|
||
constructor(uint256 _depth) { | ||
DEPTH = _depth; | ||
SIZE = 2 ** _depth; | ||
|
||
zeros[0] = bytes32(0); | ||
for (uint256 i = 1; i <= DEPTH; i++) { | ||
zeros[i] = sha256(bytes.concat(zeros[i - 1], zeros[i - 1])); | ||
} | ||
} | ||
|
||
function insertLeaf(bytes32 _leaf) external override(IFrontier) { | ||
uint256 level = _computeLevel(nextIndex); | ||
bytes32 right = _leaf; | ||
for (uint256 i = 0; i < level; i++) { | ||
right = sha256(bytes.concat(frontier[i], right)); | ||
} | ||
frontier[level] = right; | ||
nextIndex++; | ||
} | ||
|
||
function root() external view override(IFrontier) returns (bytes32) { | ||
uint256 next = nextIndex; | ||
if (next == 0) { | ||
return zeros[DEPTH]; | ||
} | ||
if (next == SIZE) { | ||
return frontier[DEPTH]; | ||
} | ||
|
||
uint256 index = next - 1; | ||
uint256 level = _computeLevel(index); | ||
|
||
// We should start at the highest frontier level with a left leaf | ||
bytes32 temp = frontier[level]; | ||
|
||
uint256 bits = index >> level; | ||
for (uint256 i = level; i < DEPTH; i++) { | ||
bool isRight = bits & 1 == 1; | ||
if (isRight) { | ||
if (frontier[i] == temp) { | ||
// We will never hit the case that frontier[i] == temp | ||
// because this require that frontier[i] is the right child | ||
// and in that case we started higher up the tree | ||
revert("Mistakes were made"); | ||
} | ||
temp = sha256(bytes.concat(frontier[i], temp)); | ||
} else { | ||
temp = sha256(bytes.concat(temp, zeros[i])); | ||
} | ||
bits >>= 1; | ||
} | ||
|
||
return temp; | ||
} | ||
|
||
function _computeLevel(uint256 _leafIndex) internal pure returns (uint256) { | ||
// The number of trailing ones is how many times in a row we are the right child. | ||
// e.g., each time this happens we go another layer up to update the parent. | ||
uint256 count = 0; | ||
uint256 index = _leafIndex; | ||
while (index & 1 == 1) { | ||
count++; | ||
index >>= 1; | ||
} | ||
return count; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright 2023 Aztec Labs. | ||
pragma solidity >=0.8.18; | ||
|
||
import {Test} from "forge-std/Test.sol"; | ||
|
||
import {NaiveMerkle} from "./Naive.sol"; | ||
import {FrontierMerkle} from "./../../src/core/messagebridge/frontier_tree/Frontier.sol"; | ||
|
||
contract MerkleTest is Test { | ||
NaiveMerkle internal merkle; | ||
FrontierMerkle internal frontier; | ||
|
||
uint256 public constant DEPTH = 10; | ||
|
||
function setUp() public { | ||
merkle = new NaiveMerkle(DEPTH); | ||
frontier = new FrontierMerkle(DEPTH); | ||
} | ||
|
||
function testFrontier() public { | ||
uint256 upper = frontier.SIZE(); | ||
for (uint256 i = 0; i < upper; i++) { | ||
bytes32 leaf = sha256(abi.encode(i + 1)); | ||
merkle.insertLeaf(leaf); | ||
frontier.insertLeaf(leaf); | ||
assertEq(merkle.computeRoot(), frontier.root(), "Frontier Roots should be equal"); | ||
} | ||
} | ||
} |
Oops, something went wrong.