-
Notifications
You must be signed in to change notification settings - Fork 43
/
Copy pathMurkyBase.sol
199 lines (183 loc) · 5.94 KB
/
MurkyBase.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
abstract contract MurkyBase {
/**
*
* CONSTRUCTOR *
*
*/
constructor() {}
/**
*
* VIRTUAL HASHING FUNCTIONS *
*
*/
function hashLeafPairs(bytes32 left, bytes32 right) public pure virtual returns (bytes32 _hash);
/**
*
* PROOF VERIFICATION *
*
*/
function verifyProof(bytes32 root, bytes32[] memory proof, bytes32 valueToProve)
external
pure
virtual
returns (bool)
{
// proof length must be less than max array size
bytes32 rollingHash = valueToProve;
uint256 length = proof.length;
unchecked {
for (uint256 i = 0; i < length; ++i) {
rollingHash = hashLeafPairs(rollingHash, proof[i]);
}
}
return root == rollingHash;
}
/**
*
* PROOF GENERATION *
*
*/
function getRoot(bytes32[] memory data) public pure virtual returns (bytes32) {
require(data.length > 1, "won't generate root for single leaf");
while (data.length > 1) {
data = hashLevel(data);
}
return data[0];
}
function getProof(bytes32[] memory data, uint256 node) public pure virtual returns (bytes32[] memory) {
require(data.length > 1, "won't generate proof for single leaf");
// The size of the proof is equal to the ceiling of log2(numLeaves)
bytes32[] memory result = new bytes32[](log2ceilBitMagic(data.length));
uint256 pos = 0;
// Two overflow risks: node, pos
// node: max array size is 2**256-1. Largest index in the array will be 1 less than that. Also,
// for dynamic arrays, size is limited to 2**64-1
// pos: pos is bounded by log2(data.length), which should be less than type(uint256).max
while (data.length > 1) {
unchecked {
if (node & 0x1 == 1) {
result[pos] = data[node - 1];
} else if (node + 1 == data.length) {
result[pos] = bytes32(0);
} else {
result[pos] = data[node + 1];
}
++pos;
node /= 2;
}
data = hashLevel(data);
}
return result;
}
///@dev function is private to prevent unsafe data from being passed
function hashLevel(bytes32[] memory data) private pure returns (bytes32[] memory) {
bytes32[] memory result;
// Function is private, and all internal callers check that data.length >=2.
// Underflow is not possible as lowest possible value for data/result index is 1
// overflow should be safe as length is / 2 always.
unchecked {
uint256 length = data.length;
if (length & 0x1 == 1) {
result = new bytes32[](length / 2 + 1);
result[result.length - 1] = hashLeafPairs(data[length - 1], bytes32(0));
} else {
result = new bytes32[](length / 2);
}
// pos is upper bounded by data.length / 2, so safe even if array is at max size
uint256 pos = 0;
for (uint256 i = 0; i < length - 1; i += 2) {
result[pos] = hashLeafPairs(data[i], data[i + 1]);
++pos;
}
}
return result;
}
/**
*
* MATH "LIBRARY" *
*
*/
/// @dev Note that x is assumed > 0
function log2ceil(uint256 x) public pure returns (uint256) {
uint256 ceil = 0;
uint256 pOf2;
// If x is a power of 2, then this function will return a ceiling
// that is 1 greater than the actual ceiling. So we need to check if
// x is a power of 2, and subtract one from ceil if so.
assembly {
// we check by seeing if x == (~x + 1) & x. This applies a mask
// to find the lowest set bit of x and then checks it for equality
// with x. If they are equal, then x is a power of 2.
/* Example
x has single bit set
x := 0000_1000
(~x + 1) = (1111_0111) + 1 = 1111_1000
(1111_1000 & 0000_1000) = 0000_1000 == x
x has multiple bits set
x := 1001_0010
(~x + 1) = (0110_1101 + 1) = 0110_1110
(0110_1110 & x) = 0000_0010 != x
*/
// we do some assembly magic to treat the bool as an integer later on
pOf2 := eq(and(add(not(x), 1), x), x)
}
// if x == type(uint256).max, than ceil is capped at 256
// if x == 0, then pO2 == 0, so ceil won't underflow
unchecked {
while (x > 0) {
x >>= 1;
ceil++;
}
ceil -= pOf2; // see above
}
return ceil;
}
/// Original bitmagic adapted from https://github.com/paulrberg/prb-math/blob/main/contracts/PRBMath.sol
/// @dev Note that x assumed > 1
function log2ceilBitMagic(uint256 x) public pure returns (uint256) {
if (x <= 1) {
return 0;
}
uint256 msb = 0;
uint256 _x = x;
if (x >= 2 ** 128) {
x >>= 128;
msb += 128;
}
if (x >= 2 ** 64) {
x >>= 64;
msb += 64;
}
if (x >= 2 ** 32) {
x >>= 32;
msb += 32;
}
if (x >= 2 ** 16) {
x >>= 16;
msb += 16;
}
if (x >= 2 ** 8) {
x >>= 8;
msb += 8;
}
if (x >= 2 ** 4) {
x >>= 4;
msb += 4;
}
if (x >= 2 ** 2) {
x >>= 2;
msb += 2;
}
if (x >= 2 ** 1) {
msb += 1;
}
uint256 lsb = (~_x + 1) & _x;
if ((lsb == _x) && (msb > 0)) {
return msb;
} else {
return msb + 1;
}
}
}