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!: use a struct for namespace and rename namespaceID #175

Merged
merged 10 commits into from
Sep 8, 2023
8 changes: 5 additions & 3 deletions src/lib/tree/Constants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ library Constants {
/// @dev The prefixes of leaves and nodes
bytes1 internal constant LEAF_PREFIX = 0x00;
bytes1 internal constant NODE_PREFIX = 0x01;
}

/// @dev Parity share namespace ID
NamespaceID internal constant PARITY_SHARE_NAMESPACE_ID =
NamespaceID.wrap(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
/// @dev Parity share namespace.
/// utility function to provide the parity share namespace as a Namespace struct.
function PARITY_SHARE_NAMESPACE() pure returns (Namespace memory) {
adlerjohn marked this conversation as resolved.
Show resolved Hide resolved
return Namespace(0xFF, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
}
37 changes: 27 additions & 10 deletions src/lib/tree/Types.sol
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;

type NamespaceID is bytes29;
/// @notice A representation of the Celestia-app namespace ID and its version.
/// See: https://celestiaorg.github.io/celestia-app/specs/namespace.html
struct Namespace {
// The namespace version.
bytes1 version;
// The namespace ID.
bytes28 id;
}

using {equalTo, lessThan, greaterThan, toBytes} for Namespace global;

using {equality as ==} for NamespaceID global;
using {lessthan as <} for NamespaceID global;
using {greaterthan as >} for NamespaceID global;
function equalTo(Namespace memory l, Namespace memory r) pure returns (bool) {
return l.toBytes() == r.toBytes();
}

function lessThan(Namespace memory l, Namespace memory r) pure returns (bool) {
return l.toBytes() < r.toBytes();
}

function equality(NamespaceID l, NamespaceID r) pure returns (bool) {
return NamespaceID.unwrap(l) == NamespaceID.unwrap(r);
function greaterThan(Namespace memory l, Namespace memory r) pure returns (bool) {
return l.toBytes() > r.toBytes();
}

function lessthan(NamespaceID l, NamespaceID r) pure returns (bool) {
return NamespaceID.unwrap(l) < NamespaceID.unwrap(r);
function toBytes(Namespace memory n) pure returns (bytes29) {
return bytes29(abi.encodePacked(n.version, n.id));
}

function greaterthan(NamespaceID l, NamespaceID r) pure returns (bool) {
return NamespaceID.unwrap(l) > NamespaceID.unwrap(r);
function toNamespace(bytes29 n) pure returns (Namespace memory) {
adlerjohn marked this conversation as resolved.
Show resolved Hide resolved
bytes memory id = new bytes(28);
for (uint256 i = 1; i < 29; i++) {
id[i - 1] = n[i];
}
return Namespace(n[0], bytes28(id));
}
14 changes: 7 additions & 7 deletions src/lib/tree/namespace/NamespaceMerkleTree.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ library NamespaceMerkleTree {
/// @notice Verify if element exists in Merkle tree, given data, proof, and root.
/// @param root The root of the tree in which the given leaf is verified.
/// @param proof Namespace Merkle proof for the leaf.
/// @param minmaxNID Namespace ID of the leaf.
/// @param namespace Namespace of the leaf.
/// @param data The data of the leaf to verify.
/// @return `true` if the proof is valid, `false` otherwise.
/// @dev proof.numLeaves is necessary to determine height of subtree containing the data to prove.
function verify(
NamespaceNode memory root,
NamespaceMerkleProof memory proof,
NamespaceID minmaxNID,
Namespace memory namespace,
bytes memory data
) internal pure returns (bool) {
// A sibling at height 1 is created by getting the leafDigest of the original data.
NamespaceNode memory node = leafDigest(minmaxNID, data);
NamespaceNode memory node = leafDigest(namespace, data);

// Since we're verifying a leaf, height parameter is 1.
return verifyInner(root, proof, node, 1);
Expand Down Expand Up @@ -137,19 +137,19 @@ library NamespaceMerkleTree {
/// @notice Verify if contiguous elements exists in Merkle tree, given leaves, mutliproof, and root.
/// @param root The root of the tree in which the given leaves are verified.
/// @param proof Namespace Merkle multiproof for the leaves.
/// @param minmaxNID Namespace ID of the leaves. All leaves must have the same namespace ID.
/// @param data The leaves to verify. Note: leaf data must be the _entire_ share (including namespace ID prefixing).
/// @param namespace Namespace of the leaves. All leaves must have the same namespace.
/// @param data The leaves to verify. Note: leaf data must be the _entire_ share (including namespace prefixing).
/// @return `true` if the proof is valid, `false` otherwise.
function verifyMulti(
NamespaceNode memory root,
NamespaceMerkleMultiproof memory proof,
NamespaceID minmaxNID,
Namespace memory namespace,
bytes[] memory data
) internal pure returns (bool) {
// Hash all the leaves to get leaf nodes.
NamespaceNode[] memory nodes = new NamespaceNode[](data.length);
for (uint256 i = 0; i < data.length; ++i) {
nodes[i] = leafDigest(minmaxNID, data[i]);
nodes[i] = leafDigest(namespace, data[i]);
}

// Verify inclusion of leaf nodes.
Expand Down
10 changes: 5 additions & 5 deletions src/lib/tree/namespace/NamespaceNode.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import "../Types.sol";

/// @notice Namespace Merkle Tree node.
struct NamespaceNode {
// Minimum namespace ID.
NamespaceID min;
// Maximum namespace ID.
NamespaceID max;
// Minimum namespace.
Namespace min;
// Maximum namespace.
Namespace max;
// Node value.
bytes32 digest;
}
Expand All @@ -19,5 +19,5 @@ struct NamespaceNode {
/// @return `true` is equal, `false otherwise.
// solhint-disable-next-line func-visibility
function namespaceNodeEquals(NamespaceNode memory first, NamespaceNode memory second) pure returns (bool) {
return (first.min == second.min) && (first.max == second.max) && (first.digest == second.digest);
return first.min.equalTo(second.min) && first.max.equalTo(second.max) && (first.digest == second.digest);
}
38 changes: 24 additions & 14 deletions src/lib/tree/namespace/TreeHasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import "./NamespaceNode.sol";

/// @notice Get the minimum namespace.
// solhint-disable-next-line func-visibility
function namespaceMin(NamespaceID l, NamespaceID r) pure returns (NamespaceID) {
if (l < r) {
function namespaceMin(Namespace memory l, Namespace memory r) pure returns (Namespace memory) {
if (l.lessThan(r)) {
return l;
} else {
return r;
Expand All @@ -17,22 +17,22 @@ function namespaceMin(NamespaceID l, NamespaceID r) pure returns (NamespaceID) {

/// @notice Get the maximum namespace.
// solhint-disable-next-line func-visibility
function namespaceMax(NamespaceID l, NamespaceID r) pure returns (NamespaceID) {
if (l > r) {
function namespaceMax(Namespace memory l, Namespace memory r) pure returns (Namespace memory) {
if (l.greaterThan(r)) {
return l;
} else {
return r;
}
}

/// @notice Hash a leaf node.
/// @param minmaxNID Namespace ID.
/// @param namespace Namespace of the leaf.
/// @param data Raw data of the leaf.
/// @dev More details in https://github.com/celestiaorg/celestia-specs/blob/master/src/specs/data_structures.md#namespace-merkle-tree
// solhint-disable-next-line func-visibility
function leafDigest(NamespaceID minmaxNID, bytes memory data) pure returns (NamespaceNode memory) {
bytes32 digest = sha256(abi.encodePacked(Constants.LEAF_PREFIX, minmaxNID, data));
NamespaceNode memory node = NamespaceNode(minmaxNID, minmaxNID, digest);
function leafDigest(Namespace memory namespace, bytes memory data) pure returns (NamespaceNode memory) {
bytes32 digest = sha256(abi.encodePacked(Constants.LEAF_PREFIX, namespace.toBytes(), data));
NamespaceNode memory node = NamespaceNode(namespace, namespace, digest);
return node;
}

Expand All @@ -42,17 +42,27 @@ function leafDigest(NamespaceID minmaxNID, bytes memory data) pure returns (Name
/// @dev More details in https://github.com/celestiaorg/celestia-specs/blob/master/src/specs/data_structures.md#namespace-merkle-tree
// solhint-disable-next-line func-visibility
function nodeDigest(NamespaceNode memory l, NamespaceNode memory r) pure returns (NamespaceNode memory) {
NamespaceID min = namespaceMin(l.min, r.min);
NamespaceID max;
if (l.min == Constants.PARITY_SHARE_NAMESPACE_ID) {
max = Constants.PARITY_SHARE_NAMESPACE_ID;
} else if (r.min == Constants.PARITY_SHARE_NAMESPACE_ID) {
Namespace memory min = namespaceMin(l.min, r.min);
Namespace memory max;
if (l.min.equalTo(PARITY_SHARE_NAMESPACE())) {
max = PARITY_SHARE_NAMESPACE();
} else if (r.min.equalTo(PARITY_SHARE_NAMESPACE())) {
max = l.max;
} else {
max = namespaceMax(l.max, r.max);
}

bytes32 digest = sha256(abi.encodePacked(Constants.NODE_PREFIX, l.min, l.max, l.digest, r.min, r.max, r.digest));
bytes32 digest = sha256(
abi.encodePacked(
Constants.NODE_PREFIX,
l.min.toBytes(),
l.max.toBytes(),
l.digest,
r.min.toBytes(),
r.max.toBytes(),
r.digest
)
);

NamespaceNode memory node = NamespaceNode(min, max, digest);
return node;
Expand Down
24 changes: 12 additions & 12 deletions src/lib/tree/namespace/test/NamespaceMerkleMultiproof.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import "../NamespaceMerkleTree.sol";
/**
* TEST VECTORS
*
* Data blocks: namespace id, data
* Data blocks: namespace, data
* 0x0000000000000000000000000000000000000000000000000000000010 0x01
* 0x0000000000000000000000000000000000000000000000000000000010 0x02
* 0x0000000000000000000000000000000000000000000000000000000010 0x03
Expand Down Expand Up @@ -50,33 +50,33 @@ contract NamespaceMerkleMultiproofTest is DSTest {
function setUp() external {}

function assertEqNamespaceNode(NamespaceNode memory first, NamespaceNode memory second) internal {
assertEq(NamespaceID.unwrap(first.min), NamespaceID.unwrap(second.min));
assertEq(NamespaceID.unwrap(first.max), NamespaceID.unwrap(second.max));
assertTrue(first.min.equalTo(second.min));
assertTrue(first.max.equalTo(second.max));
assertEq(first.digest, second.digest);
}

/// @notice Verify inclusion of leaves 0 and 1.
function testVerifyMulti01() external {
NamespaceID nid = NamespaceID.wrap(0x0000000000000000000000000000000000000000000000000000000010);
Namespace memory nid = Namespace(0x00, 0x00000000000000000000000000000000000000000000000000000010);
NamespaceNode memory root = NamespaceNode(
NamespaceID.wrap(0x0000000000000000000000000000000000000000000000000000000010),
NamespaceID.wrap(0x0000000000000000000000000000000000000000000000000000000010),
Namespace(0x00, 0x00000000000000000000000000000000000000000000000000000010),
Namespace(0x00, 0x00000000000000000000000000000000000000000000000000000010),
0x5b3328b03a538d627db78668034089cb395f63d05b24fdf99558d36fe991d268
);
NamespaceNode[] memory sideNodes = new NamespaceNode[](3);
sideNodes[0] = NamespaceNode(
NamespaceID.wrap(0x0000000000000000000000000000000000000000000000000000000010),
NamespaceID.wrap(0x0000000000000000000000000000000000000000000000000000000010),
Namespace(0x00, 0x00000000000000000000000000000000000000000000000000000010),
Namespace(0x00, 0x00000000000000000000000000000000000000000000000000000010),
0xfdb4e3c872666aa9869a1d46c8a5a0e735becdf17c62b9c3ccf4258449475bda
);
sideNodes[1] = NamespaceNode(
NamespaceID.wrap(0x0000000000000000000000000000000000000000000000000000000010),
NamespaceID.wrap(0x0000000000000000000000000000000000000000000000000000000010),
Namespace(0x00, 0x00000000000000000000000000000000000000000000000000000010),
Namespace(0x00, 0x00000000000000000000000000000000000000000000000000000010),
0xc350aeddd5ada629057034f15d4545065213a7a28f9f9b77bdc71c4225145920
);
sideNodes[2] = NamespaceNode(
Constants.PARITY_SHARE_NAMESPACE_ID,
Constants.PARITY_SHARE_NAMESPACE_ID,
PARITY_SHARE_NAMESPACE(),
PARITY_SHARE_NAMESPACE(),
0x5aa3e7ea31995fdd38f41015275229b290a8ee4810521db766ad457b9a8373d6
);

Expand Down
Loading
Loading