Checkpoints.t.sol 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. // SPDX-License-Identifier: MIT
  2. // This file was procedurally generated from scripts/generate/templates/Checkpoints.t.js.
  3. pragma solidity ^0.8.0;
  4. import "forge-std/Test.sol";
  5. import "../../contracts/utils/Checkpoints.sol";
  6. import "../../contracts/utils/math/SafeCast.sol";
  7. contract CheckpointsHistoryTest is Test {
  8. using Checkpoints for Checkpoints.History;
  9. // Maximum gap between keys used during the fuzzing tests: the `_prepareKeys` function with make sure that
  10. // key#n+1 is in the [key#n, key#n + _KEY_MAX_GAP] range.
  11. uint8 internal constant _KEY_MAX_GAP = 64;
  12. Checkpoints.History internal _ckpts;
  13. // helpers
  14. function _boundUint32(uint32 x, uint32 min, uint32 max) internal view returns (uint32) {
  15. return SafeCast.toUint32(bound(uint256(x), uint256(min), uint256(max)));
  16. }
  17. function _prepareKeys(uint32[] memory keys, uint32 maxSpread) internal view {
  18. uint32 lastKey = 0;
  19. for (uint256 i = 0; i < keys.length; ++i) {
  20. uint32 key = _boundUint32(keys[i], lastKey, lastKey + maxSpread);
  21. keys[i] = key;
  22. lastKey = key;
  23. }
  24. }
  25. function _assertLatestCheckpoint(bool exist, uint32 key, uint224 value) internal {
  26. (bool _exist, uint32 _key, uint224 _value) = _ckpts.latestCheckpoint();
  27. assertEq(_exist, exist);
  28. assertEq(_key, key);
  29. assertEq(_value, value);
  30. }
  31. // tests
  32. function testPush(uint32[] memory keys, uint224[] memory values, uint32 pastKey) public {
  33. vm.assume(values.length > 0 && values.length <= keys.length);
  34. _prepareKeys(keys, _KEY_MAX_GAP);
  35. // initial state
  36. assertEq(_ckpts.length(), 0);
  37. assertEq(_ckpts.latest(), 0);
  38. _assertLatestCheckpoint(false, 0, 0);
  39. uint256 duplicates = 0;
  40. for (uint256 i = 0; i < keys.length; ++i) {
  41. uint32 key = keys[i];
  42. uint224 value = values[i % values.length];
  43. if (i > 0 && key == keys[i - 1]) ++duplicates;
  44. // push
  45. vm.roll(key);
  46. _ckpts.push(value);
  47. // check length & latest
  48. assertEq(_ckpts.length(), i + 1 - duplicates);
  49. assertEq(_ckpts.latest(), value);
  50. _assertLatestCheckpoint(true, key, value);
  51. }
  52. // Can't push any key in the past
  53. if (keys.length > 0) {
  54. uint32 lastKey = keys[keys.length - 1];
  55. if (lastKey > 0) {
  56. pastKey = _boundUint32(pastKey, 0, lastKey - 1);
  57. vm.roll(pastKey);
  58. vm.expectRevert();
  59. this.push(values[keys.length % values.length]);
  60. }
  61. }
  62. }
  63. // used to test reverts
  64. function push(uint224 value) external {
  65. _ckpts.push(value);
  66. }
  67. function testLookup(uint32[] memory keys, uint224[] memory values, uint32 lookup) public {
  68. vm.assume(keys.length > 0);
  69. vm.assume(values.length > 0 && values.length <= keys.length);
  70. _prepareKeys(keys, _KEY_MAX_GAP);
  71. uint32 lastKey = keys[keys.length - 1];
  72. vm.assume(lastKey > 0);
  73. lookup = _boundUint32(lookup, 0, lastKey - 1);
  74. uint224 upper = 0;
  75. for (uint256 i = 0; i < keys.length; ++i) {
  76. uint32 key = keys[i];
  77. uint224 value = values[i % values.length];
  78. // push
  79. vm.roll(key);
  80. _ckpts.push(value);
  81. // track expected result of lookups
  82. if (key <= lookup) {
  83. upper = value;
  84. }
  85. }
  86. // check lookup
  87. assertEq(_ckpts.getAtBlock(lookup), upper);
  88. assertEq(_ckpts.getAtProbablyRecentBlock(lookup), upper);
  89. vm.expectRevert();
  90. this.getAtBlock(lastKey);
  91. vm.expectRevert();
  92. this.getAtBlock(lastKey + 1);
  93. vm.expectRevert();
  94. this.getAtProbablyRecentBlock(lastKey);
  95. vm.expectRevert();
  96. this.getAtProbablyRecentBlock(lastKey + 1);
  97. }
  98. // used to test reverts
  99. function getAtBlock(uint32 key) external view {
  100. _ckpts.getAtBlock(key);
  101. }
  102. // used to test reverts
  103. function getAtProbablyRecentBlock(uint32 key) external view {
  104. _ckpts.getAtProbablyRecentBlock(key);
  105. }
  106. }
  107. contract CheckpointsTrace224Test is Test {
  108. using Checkpoints for Checkpoints.Trace224;
  109. // Maximum gap between keys used during the fuzzing tests: the `_prepareKeys` function with make sure that
  110. // key#n+1 is in the [key#n, key#n + _KEY_MAX_GAP] range.
  111. uint8 internal constant _KEY_MAX_GAP = 64;
  112. Checkpoints.Trace224 internal _ckpts;
  113. // helpers
  114. function _boundUint32(uint32 x, uint32 min, uint32 max) internal view returns (uint32) {
  115. return SafeCast.toUint32(bound(uint256(x), uint256(min), uint256(max)));
  116. }
  117. function _prepareKeys(uint32[] memory keys, uint32 maxSpread) internal view {
  118. uint32 lastKey = 0;
  119. for (uint256 i = 0; i < keys.length; ++i) {
  120. uint32 key = _boundUint32(keys[i], lastKey, lastKey + maxSpread);
  121. keys[i] = key;
  122. lastKey = key;
  123. }
  124. }
  125. function _assertLatestCheckpoint(bool exist, uint32 key, uint224 value) internal {
  126. (bool _exist, uint32 _key, uint224 _value) = _ckpts.latestCheckpoint();
  127. assertEq(_exist, exist);
  128. assertEq(_key, key);
  129. assertEq(_value, value);
  130. }
  131. // tests
  132. function testPush(uint32[] memory keys, uint224[] memory values, uint32 pastKey) public {
  133. vm.assume(values.length > 0 && values.length <= keys.length);
  134. _prepareKeys(keys, _KEY_MAX_GAP);
  135. // initial state
  136. assertEq(_ckpts.length(), 0);
  137. assertEq(_ckpts.latest(), 0);
  138. _assertLatestCheckpoint(false, 0, 0);
  139. uint256 duplicates = 0;
  140. for (uint256 i = 0; i < keys.length; ++i) {
  141. uint32 key = keys[i];
  142. uint224 value = values[i % values.length];
  143. if (i > 0 && key == keys[i - 1]) ++duplicates;
  144. // push
  145. _ckpts.push(key, value);
  146. // check length & latest
  147. assertEq(_ckpts.length(), i + 1 - duplicates);
  148. assertEq(_ckpts.latest(), value);
  149. _assertLatestCheckpoint(true, key, value);
  150. }
  151. if (keys.length > 0) {
  152. uint32 lastKey = keys[keys.length - 1];
  153. if (lastKey > 0) {
  154. pastKey = _boundUint32(pastKey, 0, lastKey - 1);
  155. vm.expectRevert();
  156. this.push(pastKey, values[keys.length % values.length]);
  157. }
  158. }
  159. }
  160. // used to test reverts
  161. function push(uint32 key, uint224 value) external {
  162. _ckpts.push(key, value);
  163. }
  164. function testLookup(uint32[] memory keys, uint224[] memory values, uint32 lookup) public {
  165. vm.assume(values.length > 0 && values.length <= keys.length);
  166. _prepareKeys(keys, _KEY_MAX_GAP);
  167. uint32 lastKey = keys.length == 0 ? 0 : keys[keys.length - 1];
  168. lookup = _boundUint32(lookup, 0, lastKey + _KEY_MAX_GAP);
  169. uint224 upper = 0;
  170. uint224 lower = 0;
  171. uint32 lowerKey = type(uint32).max;
  172. for (uint256 i = 0; i < keys.length; ++i) {
  173. uint32 key = keys[i];
  174. uint224 value = values[i % values.length];
  175. // push
  176. _ckpts.push(key, value);
  177. // track expected result of lookups
  178. if (key <= lookup) {
  179. upper = value;
  180. }
  181. // find the first key that is not smaller than the lookup key
  182. if (key >= lookup && (i == 0 || keys[i - 1] < lookup)) {
  183. lowerKey = key;
  184. }
  185. if (key == lowerKey) {
  186. lower = value;
  187. }
  188. }
  189. // check lookup
  190. assertEq(_ckpts.lowerLookup(lookup), lower);
  191. assertEq(_ckpts.upperLookup(lookup), upper);
  192. assertEq(_ckpts.upperLookupRecent(lookup), upper);
  193. }
  194. }
  195. contract CheckpointsTrace160Test is Test {
  196. using Checkpoints for Checkpoints.Trace160;
  197. // Maximum gap between keys used during the fuzzing tests: the `_prepareKeys` function with make sure that
  198. // key#n+1 is in the [key#n, key#n + _KEY_MAX_GAP] range.
  199. uint8 internal constant _KEY_MAX_GAP = 64;
  200. Checkpoints.Trace160 internal _ckpts;
  201. // helpers
  202. function _boundUint96(uint96 x, uint96 min, uint96 max) internal view returns (uint96) {
  203. return SafeCast.toUint96(bound(uint256(x), uint256(min), uint256(max)));
  204. }
  205. function _prepareKeys(uint96[] memory keys, uint96 maxSpread) internal view {
  206. uint96 lastKey = 0;
  207. for (uint256 i = 0; i < keys.length; ++i) {
  208. uint96 key = _boundUint96(keys[i], lastKey, lastKey + maxSpread);
  209. keys[i] = key;
  210. lastKey = key;
  211. }
  212. }
  213. function _assertLatestCheckpoint(bool exist, uint96 key, uint160 value) internal {
  214. (bool _exist, uint96 _key, uint160 _value) = _ckpts.latestCheckpoint();
  215. assertEq(_exist, exist);
  216. assertEq(_key, key);
  217. assertEq(_value, value);
  218. }
  219. // tests
  220. function testPush(uint96[] memory keys, uint160[] memory values, uint96 pastKey) public {
  221. vm.assume(values.length > 0 && values.length <= keys.length);
  222. _prepareKeys(keys, _KEY_MAX_GAP);
  223. // initial state
  224. assertEq(_ckpts.length(), 0);
  225. assertEq(_ckpts.latest(), 0);
  226. _assertLatestCheckpoint(false, 0, 0);
  227. uint256 duplicates = 0;
  228. for (uint256 i = 0; i < keys.length; ++i) {
  229. uint96 key = keys[i];
  230. uint160 value = values[i % values.length];
  231. if (i > 0 && key == keys[i - 1]) ++duplicates;
  232. // push
  233. _ckpts.push(key, value);
  234. // check length & latest
  235. assertEq(_ckpts.length(), i + 1 - duplicates);
  236. assertEq(_ckpts.latest(), value);
  237. _assertLatestCheckpoint(true, key, value);
  238. }
  239. if (keys.length > 0) {
  240. uint96 lastKey = keys[keys.length - 1];
  241. if (lastKey > 0) {
  242. pastKey = _boundUint96(pastKey, 0, lastKey - 1);
  243. vm.expectRevert();
  244. this.push(pastKey, values[keys.length % values.length]);
  245. }
  246. }
  247. }
  248. // used to test reverts
  249. function push(uint96 key, uint160 value) external {
  250. _ckpts.push(key, value);
  251. }
  252. function testLookup(uint96[] memory keys, uint160[] memory values, uint96 lookup) public {
  253. vm.assume(values.length > 0 && values.length <= keys.length);
  254. _prepareKeys(keys, _KEY_MAX_GAP);
  255. uint96 lastKey = keys.length == 0 ? 0 : keys[keys.length - 1];
  256. lookup = _boundUint96(lookup, 0, lastKey + _KEY_MAX_GAP);
  257. uint160 upper = 0;
  258. uint160 lower = 0;
  259. uint96 lowerKey = type(uint96).max;
  260. for (uint256 i = 0; i < keys.length; ++i) {
  261. uint96 key = keys[i];
  262. uint160 value = values[i % values.length];
  263. // push
  264. _ckpts.push(key, value);
  265. // track expected result of lookups
  266. if (key <= lookup) {
  267. upper = value;
  268. }
  269. // find the first key that is not smaller than the lookup key
  270. if (key >= lookup && (i == 0 || keys[i - 1] < lookup)) {
  271. lowerKey = key;
  272. }
  273. if (key == lowerKey) {
  274. lower = value;
  275. }
  276. }
  277. // check lookup
  278. assertEq(_ckpts.lowerLookup(lookup), lower);
  279. assertEq(_ckpts.upperLookup(lookup), upper);
  280. assertEq(_ckpts.upperLookupRecent(lookup), upper);
  281. }
  282. }