Checkpoints.t.sol 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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. pastKey = _boundUint32(pastKey, 0, lastKey - 1);
  56. vm.roll(pastKey);
  57. vm.expectRevert();
  58. this.push(values[keys.length % values.length]);
  59. }
  60. }
  61. // used to test reverts
  62. function push(uint224 value) external {
  63. _ckpts.push(value);
  64. }
  65. function testLookup(uint32[] memory keys, uint224[] memory values, uint32 lookup) public {
  66. vm.assume(keys.length > 0);
  67. vm.assume(values.length > 0 && values.length <= keys.length);
  68. _prepareKeys(keys, _KEY_MAX_GAP);
  69. uint32 lastKey = keys[keys.length - 1];
  70. vm.assume(lastKey > 0);
  71. lookup = _boundUint32(lookup, 0, lastKey - 1);
  72. uint224 upper = 0;
  73. for (uint256 i = 0; i < keys.length; ++i) {
  74. uint32 key = keys[i];
  75. uint224 value = values[i % values.length];
  76. // push
  77. vm.roll(key);
  78. _ckpts.push(value);
  79. // track expected result of lookups
  80. if (key <= lookup) {
  81. upper = value;
  82. }
  83. }
  84. // check lookup
  85. assertEq(_ckpts.getAtBlock(lookup), upper);
  86. assertEq(_ckpts.getAtProbablyRecentBlock(lookup), upper);
  87. vm.expectRevert();
  88. this.getAtBlock(lastKey);
  89. vm.expectRevert();
  90. this.getAtBlock(lastKey + 1);
  91. vm.expectRevert();
  92. this.getAtProbablyRecentBlock(lastKey);
  93. vm.expectRevert();
  94. this.getAtProbablyRecentBlock(lastKey + 1);
  95. }
  96. // used to test reverts
  97. function getAtBlock(uint32 key) external view {
  98. _ckpts.getAtBlock(key);
  99. }
  100. // used to test reverts
  101. function getAtProbablyRecentBlock(uint32 key) external view {
  102. _ckpts.getAtProbablyRecentBlock(key);
  103. }
  104. }
  105. contract CheckpointsTrace224Test is Test {
  106. using Checkpoints for Checkpoints.Trace224;
  107. // Maximum gap between keys used during the fuzzing tests: the `_prepareKeys` function with make sure that
  108. // key#n+1 is in the [key#n, key#n + _KEY_MAX_GAP] range.
  109. uint8 internal constant _KEY_MAX_GAP = 64;
  110. Checkpoints.Trace224 internal _ckpts;
  111. // helpers
  112. function _boundUint32(uint32 x, uint32 min, uint32 max) internal view returns (uint32) {
  113. return SafeCast.toUint32(bound(uint256(x), uint256(min), uint256(max)));
  114. }
  115. function _prepareKeys(uint32[] memory keys, uint32 maxSpread) internal view {
  116. uint32 lastKey = 0;
  117. for (uint256 i = 0; i < keys.length; ++i) {
  118. uint32 key = _boundUint32(keys[i], lastKey, lastKey + maxSpread);
  119. keys[i] = key;
  120. lastKey = key;
  121. }
  122. }
  123. function _assertLatestCheckpoint(bool exist, uint32 key, uint224 value) internal {
  124. (bool _exist, uint32 _key, uint224 _value) = _ckpts.latestCheckpoint();
  125. assertEq(_exist, exist);
  126. assertEq(_key, key);
  127. assertEq(_value, value);
  128. }
  129. // tests
  130. function testPush(uint32[] memory keys, uint224[] memory values, uint32 pastKey) public {
  131. vm.assume(values.length > 0 && values.length <= keys.length);
  132. _prepareKeys(keys, _KEY_MAX_GAP);
  133. // initial state
  134. assertEq(_ckpts.length(), 0);
  135. assertEq(_ckpts.latest(), 0);
  136. _assertLatestCheckpoint(false, 0, 0);
  137. uint256 duplicates = 0;
  138. for (uint256 i = 0; i < keys.length; ++i) {
  139. uint32 key = keys[i];
  140. uint224 value = values[i % values.length];
  141. if (i > 0 && key == keys[i - 1]) ++duplicates;
  142. // push
  143. _ckpts.push(key, value);
  144. // check length & latest
  145. assertEq(_ckpts.length(), i + 1 - duplicates);
  146. assertEq(_ckpts.latest(), value);
  147. _assertLatestCheckpoint(true, key, value);
  148. }
  149. if (keys.length > 0) {
  150. uint32 lastKey = keys[keys.length - 1];
  151. pastKey = _boundUint32(pastKey, 0, lastKey - 1);
  152. vm.expectRevert();
  153. this.push(pastKey, values[keys.length % values.length]);
  154. }
  155. }
  156. // used to test reverts
  157. function push(uint32 key, uint224 value) external {
  158. _ckpts.push(key, value);
  159. }
  160. function testLookup(uint32[] memory keys, uint224[] memory values, uint32 lookup) public {
  161. vm.assume(values.length > 0 && values.length <= keys.length);
  162. _prepareKeys(keys, _KEY_MAX_GAP);
  163. uint32 lastKey = keys.length == 0 ? 0 : keys[keys.length - 1];
  164. lookup = _boundUint32(lookup, 0, lastKey + _KEY_MAX_GAP);
  165. uint224 upper = 0;
  166. uint224 lower = 0;
  167. uint32 lowerKey = type(uint32).max;
  168. for (uint256 i = 0; i < keys.length; ++i) {
  169. uint32 key = keys[i];
  170. uint224 value = values[i % values.length];
  171. // push
  172. _ckpts.push(key, value);
  173. // track expected result of lookups
  174. if (key <= lookup) {
  175. upper = value;
  176. }
  177. // find the first key that is not smaller than the lookup key
  178. if (key >= lookup && (i == 0 || keys[i - 1] < lookup)) {
  179. lowerKey = key;
  180. }
  181. if (key == lowerKey) {
  182. lower = value;
  183. }
  184. }
  185. // check lookup
  186. assertEq(_ckpts.lowerLookup(lookup), lower);
  187. assertEq(_ckpts.upperLookup(lookup), upper);
  188. assertEq(_ckpts.upperLookupRecent(lookup), upper);
  189. }
  190. }
  191. contract CheckpointsTrace160Test is Test {
  192. using Checkpoints for Checkpoints.Trace160;
  193. // Maximum gap between keys used during the fuzzing tests: the `_prepareKeys` function with make sure that
  194. // key#n+1 is in the [key#n, key#n + _KEY_MAX_GAP] range.
  195. uint8 internal constant _KEY_MAX_GAP = 64;
  196. Checkpoints.Trace160 internal _ckpts;
  197. // helpers
  198. function _boundUint96(uint96 x, uint96 min, uint96 max) internal view returns (uint96) {
  199. return SafeCast.toUint96(bound(uint256(x), uint256(min), uint256(max)));
  200. }
  201. function _prepareKeys(uint96[] memory keys, uint96 maxSpread) internal view {
  202. uint96 lastKey = 0;
  203. for (uint256 i = 0; i < keys.length; ++i) {
  204. uint96 key = _boundUint96(keys[i], lastKey, lastKey + maxSpread);
  205. keys[i] = key;
  206. lastKey = key;
  207. }
  208. }
  209. function _assertLatestCheckpoint(bool exist, uint96 key, uint160 value) internal {
  210. (bool _exist, uint96 _key, uint160 _value) = _ckpts.latestCheckpoint();
  211. assertEq(_exist, exist);
  212. assertEq(_key, key);
  213. assertEq(_value, value);
  214. }
  215. // tests
  216. function testPush(uint96[] memory keys, uint160[] memory values, uint96 pastKey) public {
  217. vm.assume(values.length > 0 && values.length <= keys.length);
  218. _prepareKeys(keys, _KEY_MAX_GAP);
  219. // initial state
  220. assertEq(_ckpts.length(), 0);
  221. assertEq(_ckpts.latest(), 0);
  222. _assertLatestCheckpoint(false, 0, 0);
  223. uint256 duplicates = 0;
  224. for (uint256 i = 0; i < keys.length; ++i) {
  225. uint96 key = keys[i];
  226. uint160 value = values[i % values.length];
  227. if (i > 0 && key == keys[i - 1]) ++duplicates;
  228. // push
  229. _ckpts.push(key, value);
  230. // check length & latest
  231. assertEq(_ckpts.length(), i + 1 - duplicates);
  232. assertEq(_ckpts.latest(), value);
  233. _assertLatestCheckpoint(true, key, value);
  234. }
  235. if (keys.length > 0) {
  236. uint96 lastKey = keys[keys.length - 1];
  237. pastKey = _boundUint96(pastKey, 0, lastKey - 1);
  238. vm.expectRevert();
  239. this.push(pastKey, values[keys.length % values.length]);
  240. }
  241. }
  242. // used to test reverts
  243. function push(uint96 key, uint160 value) external {
  244. _ckpts.push(key, value);
  245. }
  246. function testLookup(uint96[] memory keys, uint160[] memory values, uint96 lookup) public {
  247. vm.assume(values.length > 0 && values.length <= keys.length);
  248. _prepareKeys(keys, _KEY_MAX_GAP);
  249. uint96 lastKey = keys.length == 0 ? 0 : keys[keys.length - 1];
  250. lookup = _boundUint96(lookup, 0, lastKey + _KEY_MAX_GAP);
  251. uint160 upper = 0;
  252. uint160 lower = 0;
  253. uint96 lowerKey = type(uint96).max;
  254. for (uint256 i = 0; i < keys.length; ++i) {
  255. uint96 key = keys[i];
  256. uint160 value = values[i % values.length];
  257. // push
  258. _ckpts.push(key, value);
  259. // track expected result of lookups
  260. if (key <= lookup) {
  261. upper = value;
  262. }
  263. // find the first key that is not smaller than the lookup key
  264. if (key >= lookup && (i == 0 || keys[i - 1] < lookup)) {
  265. lowerKey = key;
  266. }
  267. if (key == lowerKey) {
  268. lower = value;
  269. }
  270. }
  271. // check lookup
  272. assertEq(_ckpts.lowerLookup(lookup), lower);
  273. assertEq(_ckpts.upperLookup(lookup), upper);
  274. assertEq(_ckpts.upperLookupRecent(lookup), upper);
  275. }
  276. }