Skip to content

Commit 8d63e0b

Browse files
committed
use index for cid most places
1 parent 4894b4b commit 8d63e0b

File tree

11 files changed

+169
-114
lines changed

11 files changed

+169
-114
lines changed

src/CborDecode.sol

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ uint8 constant MajMap = 5;
2929
uint8 constant MajTag = 6;
3030
uint8 constant MajOther = 7;
3131

32-
uint8 constant True_Type = 21;
3332
uint8 constant False_Type = 20;
33+
uint8 constant True_Type = 21;
34+
uint8 constant Null_Type = 22;
3435

3536
/// @notice This library is a set a functions that allows anyone to decode cbor encoded bytes
3637
/// @dev methods in this library try to read the data type indicated from cbor encoded data stored in bytes at a specific index
@@ -41,7 +42,7 @@ library CBORDecoder {
4142
/// @param cborData cbor encoded bytes to parse from
4243
/// @param byteIdx current position to read on the cbor encoded bytes
4344
function isNullNext(bytes memory cborData, uint byteIdx) internal pure returns (bool) {
44-
return cborData[byteIdx] == hex"f6";
45+
return cborData[byteIdx] == hex"f6"; // MajSimple 0xe0 & Null_Type 0x16 == 0xf6
4546
}
4647

4748
/// @notice attempt to read a bool value

src/CidCbor.sol

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,85 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.28;
33

4+
import {console} from "forge-std/Test.sol";
45
import "./CborDecode.sol";
56
import "./Compare.sol";
67

78
library CidCbor {
89
using CBORDecoder for bytes;
910

11+
uint8 private constant MAJOR_BYTE_STRING = 2;
1012
uint8 private constant MAJOR_TYPE_TAG = 6;
1113
uint8 private constant TAG_CID = 42;
1214

15+
uint8 private constant EXPECTED_CID_LENGTH = 37;
16+
1317
uint8 private constant MULTIBASE_FORMAT = 0x00;
1418
uint8 private constant CID_V1 = 0x01;
1519
uint8 private constant MULTICODEC_DAG_CBOR = 0x71;
1620
uint8 private constant MULTIHASH_SHA_256 = 0x12;
1721
uint8 private constant MULTIHASH_SIZE_32 = 0x20;
1822

19-
struct Cid {
20-
bytes4 prefix;
21-
bytes32 sha;
22-
bool nullish;
23-
}
23+
/**
24+
* will only encounter Cid v1 dag-cbor sha256, so we can refer to cid within
25+
* a larger bytestring by index of its sha256 hash segment. the caller is
26+
* responsible for indexing the correct bytestring when retrieving the hash.
27+
* due to cbor and cid formats, the hash segment will never index at 0, so
28+
* so 0 is used to indicate a null value.
29+
*/
30+
type CidIndex is uint;
2431

25-
function cidMatches(Cid memory one, Cid memory other) internal pure returns (bool) {
26-
require(!(one.nullish && other.nullish), "two nullish cids should not be compared");
27-
return Compare.bytesMatch(
28-
abi.encodePacked(one.prefix, one.sha, one.nullish), abi.encodePacked(other.prefix, other.sha, other.nullish)
29-
);
30-
}
32+
/**
33+
* we will only encounter Cid v1 dag-cbor sha256, so the hash is bytes32
34+
*/
35+
type CidBytes32 is bytes32;
3136

3237
function expectCidTag(bytes memory cborData, uint byteIdx) internal pure returns (uint) {
38+
console.log("expectCidTag", byteIdx);
3339
uint8 maj;
3440
uint value;
3541
(maj, value, byteIdx) = cborData.parseCborHeader(byteIdx);
36-
require(maj == MAJOR_TYPE_TAG, "expected major type tag");
37-
require(value == TAG_CID, "expected tag for CID");
42+
require(maj == MAJOR_TYPE_TAG, "expected tag major");
43+
require(value == TAG_CID, "expected tag 42 for CID");
3844
return byteIdx;
3945
}
4046

41-
function readCid(bytes memory cborData, uint byteIdx) internal pure returns (Cid memory, uint) {
42-
bool nullish = false;
43-
44-
if (cborData.isNullNext(byteIdx)) {
45-
nullish = true;
46-
return (Cid(bytes4(0), bytes32(0), nullish), byteIdx + 1);
47+
function readCidBytes32(bytes memory cborData, CidIndex idx) internal pure returns (CidBytes32) {
48+
uint cidIdx = CidIndex.unwrap(idx);
49+
require(cidIdx != 0, "Can't read a CID hash at index 0");
50+
bytes memory cidBytes = new bytes(MULTIHASH_SIZE_32);
51+
for (uint i = 0; i < MULTIHASH_SIZE_32; i++) {
52+
cidBytes[i] = cborData[cidIdx + i];
4753
}
54+
return CidBytes32.wrap(bytes32(cidBytes));
55+
}
4856

49-
(byteIdx) = CidCbor.expectCidTag(cborData, byteIdx);
50-
57+
function readNullableCidIndex(bytes memory cborData, uint byteIdx) internal pure returns (CidIndex, uint) {
58+
console.log("readNullableCid", byteIdx);
5159
if (cborData.isNullNext(byteIdx)) {
52-
nullish = true;
53-
return (Cid(bytes4(0), bytes32(0), nullish), byteIdx + 1);
60+
console.log("nullish %s, advancing %s + 1", uint8(cborData[byteIdx]), byteIdx);
61+
return (CidIndex.wrap(0), byteIdx + 1);
5462
}
63+
console.log("not nullish %s, not advancing %s", uint8(cborData[byteIdx]), byteIdx);
5564

56-
bytes memory cidBytes;
57-
(cidBytes, byteIdx) = cborData.readBytes(byteIdx);
58-
59-
// multibase format
60-
require(uint8(cidBytes[0]) == MULTIBASE_FORMAT, "expected multibase item");
61-
62-
// cid prefix
63-
require(uint8(cidBytes[1]) == CID_V1, "expected CID v1");
64-
require(uint8(cidBytes[2]) == MULTICODEC_DAG_CBOR, "expected CID multicodec DAG-CBOR");
65-
require(uint8(cidBytes[3]) == MULTIHASH_SHA_256, "expected CID multihash sha-256");
66-
require(uint8(cidBytes[4]) == MULTIHASH_SIZE_32, "expected CID content size 32 bytes");
67-
68-
bytes4 prefix = bytes4(abi.encodePacked(cidBytes[1], cidBytes[2], cidBytes[3], cidBytes[4]));
65+
return readCidIndex(cborData, byteIdx);
66+
}
6967

70-
// cid data length plus prefix length
71-
require(cidBytes.length == 1 + 4 + MULTIHASH_SIZE_32, "expected cid data to be 37 bytes");
68+
function readCidIndex(bytes memory cborData, uint byteIdx) internal pure returns (CidIndex, uint) {
69+
(byteIdx) = expectCidTag(cborData, byteIdx);
70+
(uint8 maj, uint len, uint bytesStart) = cborData.parseCborHeader(byteIdx);
7271

73-
bytes memory sha = new bytes(32);
74-
for (uint i = 0; i < 32; i++) {
75-
sha[i] = cidBytes[5 + i];
76-
}
72+
require(maj == MAJOR_BYTE_STRING, "expected byte string");
73+
require(len == EXPECTED_CID_LENGTH, "expected bytes length 37 for CID");
7774

78-
return (Cid(prefix, bytes32(sha), nullish), byteIdx);
75+
// multibase format
76+
require(uint8(cborData[bytesStart]) == MULTIBASE_FORMAT, "expected multibase item");
77+
// cid format
78+
require(uint8(cborData[bytesStart + 1]) == CID_V1, "expected CID v1");
79+
require(uint8(cborData[bytesStart + 2]) == MULTICODEC_DAG_CBOR, "expected CID multicodec DAG-CBOR");
80+
require(uint8(cborData[bytesStart + 3]) == MULTIHASH_SHA_256, "expected CID multihash sha-256");
81+
require(uint8(cborData[bytesStart + 4]) == MULTIHASH_SIZE_32, "expected CID content size 32 bytes");
82+
83+
return (CidIndex.wrap(bytesStart + 5), bytesStart + len);
7984
}
8085
}

src/CommitCbor.sol

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ library CommitCbor {
1212
struct Commit {
1313
string did;
1414
uint8 version;
15-
CidCbor.Cid data;
15+
CidCbor.CidIndex data;
1616
string rev;
17-
CidCbor.Cid prev;
17+
CidCbor.CidIndex prev;
1818
}
1919

2020
function readCommit(bytes memory cborData, uint byteIdx) internal pure returns (Commit memory ret, uint) {
@@ -33,11 +33,13 @@ library CommitCbor {
3333
(ret.version, byteIdx) = cborData.readUInt8(byteIdx);
3434
require(ret.version == COMMIT_VERSION, "unexpected commit version");
3535
} else if (Compare.stringsMatch(mapKey, "data")) {
36-
(ret.data, byteIdx) = CidCbor.readCid(cborData, byteIdx);
36+
console.log("data", byteIdx);
37+
(ret.data, byteIdx) = CidCbor.readCidIndex(cborData, byteIdx);
3738
} else if (Compare.stringsMatch(mapKey, "rev")) {
3839
(ret.rev, byteIdx) = cborData.readString(byteIdx);
3940
} else if (Compare.stringsMatch(mapKey, "prev")) {
40-
(ret.prev, byteIdx) = CidCbor.readCid(cborData, byteIdx);
41+
console.log("prev", byteIdx);
42+
(ret.prev, byteIdx) = CidCbor.readNullableCidIndex(cborData, byteIdx);
4143
}
4244
}
4345

src/TreeCbor.sol

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ library TreeCbor {
1515
uint8 private constant MULTIHASH_SIZE_32 = 0x20;
1616

1717
struct Tree {
18-
CidCbor.Cid root;
18+
CidCbor.CidBytes32 root;
1919
TreeNodeCbor.TreeNode[] nodes;
2020
RecordCbor.Record[] records;
21-
CidCbor.Cid[] cids;
21+
CidCbor.CidBytes32[] cids;
2222
bool[] nodeOrRecord;
2323
}
2424

@@ -30,18 +30,14 @@ library TreeCbor {
3030
return len == 2;
3131
}
3232

33-
function readTree(bytes[] memory cborData, CidCbor.Cid memory root) internal pure returns (Tree memory) {
33+
function readTree(bytes[] memory cborData, CidCbor.CidBytes32 root) internal pure returns (Tree memory) {
3434
TreeNodeCbor.TreeNode[] memory nodes = new TreeNodeCbor.TreeNode[](cborData.length);
3535
RecordCbor.Record[] memory records = new RecordCbor.Record[](cborData.length);
36-
CidCbor.Cid[] memory cids = new CidCbor.Cid[](cborData.length);
36+
CidCbor.CidBytes32[] memory cids = new CidCbor.CidBytes32[](cborData.length);
3737
bool[] memory nodeOrRecord = new bool[](cborData.length);
3838

3939
for (uint i = 0; i < cborData.length; i++) {
40-
cids[i] = CidCbor.Cid(
41-
bytes4(abi.encodePacked(CID_V1, MULTICODEC_DAG_CBOR, MULTIHASH_SHA_256, MULTIHASH_SIZE_32)),
42-
sha256(cborData[i]),
43-
false
44-
);
40+
cids[i] = CidCbor.CidBytes32.wrap(sha256(cborData[i]));
4541
uint byteIdx;
4642
if (itemIsNode(cborData[i])) {
4743
(nodes[i], byteIdx) = TreeNodeCbor.readTreeNode(cborData[i], 0);
@@ -56,43 +52,46 @@ library TreeCbor {
5652
return requireUniqueCidsAndRoot(Tree(root, nodes, records, cids, nodeOrRecord));
5753
}
5854

59-
function nodeByCid(Tree memory tree, CidCbor.Cid memory indexCid)
55+
function nodeByCid(Tree memory tree, CidCbor.CidBytes32 indexCid)
6056
internal
6157
pure
6258
returns (TreeNodeCbor.TreeNode memory, uint index)
6359
{
6460
return nodeByCid(tree, indexCid, 0);
6561
}
6662

67-
function nodeByCid(Tree memory tree, CidCbor.Cid memory indexCid, uint startIdx)
63+
function nodeByCid(Tree memory tree, CidCbor.CidBytes32 indexCid, uint startIdx)
6864
internal
6965
pure
7066
returns (TreeNodeCbor.TreeNode memory, uint index)
7167
{
68+
bytes32 indexBytes = CidCbor.CidBytes32.unwrap(indexCid);
7269
for (uint i = startIdx; i < tree.cids.length; i++) {
73-
if (CidCbor.cidMatches(tree.cids[i], indexCid)) {
70+
if (CidCbor.CidBytes32.unwrap(tree.cids[i]) == indexBytes) {
7471
require(tree.nodeOrRecord[i], "expected node");
7572
return (tree.nodes[i], i);
7673
}
7774
}
7875
revert("node not found");
7976
}
8077

81-
function recordByCid(Tree memory tree, CidCbor.Cid memory indexCid)
78+
function recordByCid(Tree memory tree, CidCbor.CidBytes32 indexCid)
8279
internal
8380
pure
8481
returns (RecordCbor.Record memory, uint index)
8582
{
8683
return recordByCid(tree, indexCid, 0);
8784
}
8885

89-
function recordByCid(Tree memory tree, CidCbor.Cid memory indexCid, uint startIdx)
86+
function recordByCid(Tree memory tree, CidCbor.CidBytes32 indexCid, uint startIdx)
9087
internal
9188
pure
9289
returns (RecordCbor.Record memory, uint index)
9390
{
91+
bytes32 indexBytes = CidCbor.CidBytes32.unwrap(indexCid);
92+
9493
for (uint i = startIdx; i < tree.cids.length; i++) {
95-
if (CidCbor.cidMatches(tree.cids[i], indexCid)) {
94+
if (CidCbor.CidBytes32.unwrap(tree.cids[i]) == indexBytes) {
9695
require(!tree.nodeOrRecord[i], "expected record");
9796
return (tree.records[i], i);
9897
}
@@ -102,12 +101,15 @@ library TreeCbor {
102101

103102
function requireUniqueCidsAndRoot(Tree memory tree) internal pure returns (Tree memory) {
104103
bool rootIncluded;
104+
bytes32 treeRoot = CidCbor.CidBytes32.unwrap(tree.root);
105105
for (uint i = 0; i < tree.cids.length; i++) {
106+
bytes32 thisCid = CidCbor.CidBytes32.unwrap(tree.cids[i]);
106107
if (!rootIncluded) {
107-
rootIncluded = CidCbor.cidMatches(tree.cids[i], tree.root);
108+
rootIncluded = thisCid == treeRoot;
108109
}
109110
for (uint j = i + 1; j < tree.nodes.length; j++) {
110-
require(!CidCbor.cidMatches(tree.cids[i], tree.cids[j]), "node cids must be unique");
111+
bytes32 otherCid = CidCbor.CidBytes32.unwrap(tree.cids[j]);
112+
require(thisCid != otherCid, "node cids must be unique");
111113
}
112114
}
113115
require(rootIncluded, "root cid must be included");

src/TreeNodeCbor.sol

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.28;
33

4+
import {console} from "forge-std/Test.sol";
45
import "./CidCbor.sol";
56
import "./Compare.sol";
67

78
library TreeNodeCbor {
89
using CBORDecoder for bytes;
910

1011
struct TreeNode {
11-
CidCbor.Cid left;
12+
CidCbor.CidIndex left;
1213
TreeNodeEntry[] entries;
1314
}
1415

1516
struct TreeNodeEntry {
1617
string key;
17-
CidCbor.Cid value;
18-
CidCbor.Cid tree;
18+
CidCbor.CidIndex value;
19+
CidCbor.CidIndex tree;
1920
}
2021

2122
struct TreeNodeE {
2223
uint8 p; // prefixlen
2324
bytes k; // keysuffix
24-
CidCbor.Cid v; // value
25-
CidCbor.Cid t; // tree
25+
CidCbor.CidIndex v; // value
26+
CidCbor.CidIndex t; // tree
2627
}
2728

2829
function readNodeE(bytes memory cborData, uint byteIdx) internal pure returns (TreeNodeE[] memory ret, uint) {
@@ -45,8 +46,8 @@ library TreeNodeCbor {
4546

4647
uint8 p;
4748
bytes memory k;
48-
CidCbor.Cid memory v;
49-
CidCbor.Cid memory t;
49+
CidCbor.CidIndex v;
50+
CidCbor.CidIndex t;
5051

5152
for (uint i = 0; i < mapLen; i++) {
5253
string memory mapKey;
@@ -56,9 +57,11 @@ library TreeNodeCbor {
5657
} else if (Compare.stringsMatch(mapKey, "k")) {
5758
(k, byteIdx) = cborData.readBytes(byteIdx);
5859
} else if (Compare.stringsMatch(mapKey, "t")) {
59-
(t, byteIdx) = CidCbor.readCid(cborData, byteIdx);
60+
console.log("t");
61+
(t, byteIdx) = CidCbor.readNullableCidIndex(cborData, byteIdx);
6062
} else if (Compare.stringsMatch(mapKey, "v")) {
61-
(v, byteIdx) = CidCbor.readCid(cborData, byteIdx);
63+
console.log("v");
64+
(v, byteIdx) = CidCbor.readNullableCidIndex(cborData, byteIdx);
6265
}
6366
}
6467

@@ -92,7 +95,8 @@ library TreeNodeCbor {
9295
string memory mapKey;
9396
(mapKey, byteIdx) = cborData.readString(byteIdx);
9497
if (Compare.stringsMatch(mapKey, "l")) {
95-
(node.left, byteIdx) = CidCbor.readCid(cborData, byteIdx);
98+
console.log("l");
99+
(node.left, byteIdx) = CidCbor.readNullableCidIndex(cborData, byteIdx);
96100
} else if (Compare.stringsMatch(mapKey, "e")) {
97101
TreeNodeE[] memory e;
98102
(e, byteIdx) = readNodeE(cborData, byteIdx);

test/CborDecode.t.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ contract CborDecodeTest {
3030
using CBORDecoder for bytes;
3131

3232
function test_decodeFixedArray() public pure {
33-
// [1,2,3,4,5,6,7,8,9,10,"test", h'01010101', false, null, true]
3433
bytes memory input = hex"8F0102030405060708090A64746573744401010101F4F6F5";
3534
uint index = 0;
3635
uint arrayLen = 0;

test/CidCbor.t.sol

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.17;
3+
4+
import {Test, console} from "forge-std/Test.sol";
5+
import "../src/CborDecode.sol";
6+
import "../src/CidCbor.sol";
7+
8+
contract CidCborTest {
9+
bytes private constant cidCbor =
10+
hex"D82A5825000171122066DA6655BF8DA79B69A87299CF170FED8497FA3059379DC4A8BFE1E28CAB5D93";
11+
12+
function test_readCidIndex_only() public pure {
13+
CidCbor.readCidIndex(cidCbor, 0);
14+
}
15+
16+
function test_readCidIndex_readCidBytes32() public pure {
17+
(CidCbor.CidIndex cidIdx, uint byteIdx) = CidCbor.readCidIndex(cidCbor, 0);
18+
require(byteIdx == cidCbor.length, "expected to read all bytes");
19+
20+
CidCbor.CidBytes32 cid = CidCbor.readCidBytes32(cidCbor, cidIdx);
21+
console.logBytes32(CidCbor.CidBytes32.unwrap(cid));
22+
}
23+
}

0 commit comments

Comments
 (0)