|
1 | 1 | // SPDX-License-Identifier: MIT |
2 | 2 | pragma solidity ^0.8.28; |
3 | 3 |
|
| 4 | +import {console} from "forge-std/Test.sol"; |
4 | 5 | import "./CborDecode.sol"; |
5 | 6 | import "./Compare.sol"; |
6 | 7 |
|
7 | 8 | library CidCbor { |
8 | 9 | using CBORDecoder for bytes; |
9 | 10 |
|
| 11 | + uint8 private constant MAJOR_BYTE_STRING = 2; |
10 | 12 | uint8 private constant MAJOR_TYPE_TAG = 6; |
11 | 13 | uint8 private constant TAG_CID = 42; |
12 | 14 |
|
| 15 | + uint8 private constant EXPECTED_CID_LENGTH = 37; |
| 16 | + |
13 | 17 | uint8 private constant MULTIBASE_FORMAT = 0x00; |
14 | 18 | uint8 private constant CID_V1 = 0x01; |
15 | 19 | uint8 private constant MULTICODEC_DAG_CBOR = 0x71; |
16 | 20 | uint8 private constant MULTIHASH_SHA_256 = 0x12; |
17 | 21 | uint8 private constant MULTIHASH_SIZE_32 = 0x20; |
18 | 22 |
|
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; |
24 | 31 |
|
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; |
31 | 36 |
|
32 | 37 | function expectCidTag(bytes memory cborData, uint byteIdx) internal pure returns (uint) { |
| 38 | + console.log("expectCidTag", byteIdx); |
33 | 39 | uint8 maj; |
34 | 40 | uint value; |
35 | 41 | (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"); |
38 | 44 | return byteIdx; |
39 | 45 | } |
40 | 46 |
|
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]; |
47 | 53 | } |
| 54 | + return CidBytes32.wrap(bytes32(cidBytes)); |
| 55 | + } |
48 | 56 |
|
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); |
51 | 59 | 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); |
54 | 62 | } |
| 63 | + console.log("not nullish %s, not advancing %s", uint8(cborData[byteIdx]), byteIdx); |
55 | 64 |
|
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 | + } |
69 | 67 |
|
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); |
72 | 71 |
|
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"); |
77 | 74 |
|
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); |
79 | 84 | } |
80 | 85 | } |
0 commit comments