SlotDerivation.t.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. const format = require('../format-lines');
  2. const { capitalize } = require('../../helpers');
  3. const { TYPES } = require('./Slot.opts');
  4. const header = `\
  5. pragma solidity ^0.8.20;
  6. import {Test} from "forge-std/Test.sol";
  7. import {SymTest} from "halmos-cheatcodes/SymTest.sol";
  8. import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol";
  9. `;
  10. const array = `\
  11. bytes[] private _array;
  12. function symbolicDeriveArray(uint256 length, uint256 offset) public {
  13. vm.assume(length > 0);
  14. vm.assume(offset < length);
  15. _assertDeriveArray(length, offset);
  16. }
  17. function testDeriveArray(uint256 length, uint256 offset) public {
  18. length = bound(length, 1, type(uint256).max);
  19. offset = bound(offset, 0, length - 1);
  20. _assertDeriveArray(length, offset);
  21. }
  22. function _assertDeriveArray(uint256 length, uint256 offset) public {
  23. bytes32 baseSlot;
  24. assembly {
  25. baseSlot := _array.slot
  26. sstore(baseSlot, length) // store length so solidity access does not revert
  27. }
  28. bytes storage derived = _array[offset];
  29. bytes32 derivedSlot;
  30. assembly {
  31. derivedSlot := derived.slot
  32. }
  33. assertEq(baseSlot.deriveArray().offset(offset), derivedSlot);
  34. }
  35. `;
  36. const mapping = ({ type, name }) => `\
  37. mapping(${type} => bytes) private _${type}Mapping;
  38. function testSymbolicDeriveMapping${name}(${type} key) public {
  39. bytes32 baseSlot;
  40. assembly {
  41. baseSlot := _${type}Mapping.slot
  42. }
  43. bytes storage derived = _${type}Mapping[key];
  44. bytes32 derivedSlot;
  45. assembly {
  46. derivedSlot := derived.slot
  47. }
  48. assertEq(baseSlot.deriveMapping(key), derivedSlot);
  49. }
  50. `;
  51. const boundedMapping = ({ type, name }) => `\
  52. mapping(${type} => bytes) private _${type}Mapping;
  53. function testDeriveMapping${name}(${type} memory key) public {
  54. _assertDeriveMapping${name}(key);
  55. }
  56. function symbolicDeriveMapping${name}() public {
  57. _assertDeriveMapping${name}(svm.create${name}(256, "DeriveMapping${name}Input"));
  58. }
  59. function _assertDeriveMapping${name}(${type} memory key) internal {
  60. bytes32 baseSlot;
  61. assembly {
  62. baseSlot := _${type}Mapping.slot
  63. }
  64. bytes storage derived = _${type}Mapping[key];
  65. bytes32 derivedSlot;
  66. assembly {
  67. derivedSlot := derived.slot
  68. }
  69. assertEq(baseSlot.deriveMapping(key), derivedSlot);
  70. }
  71. `;
  72. // GENERATE
  73. module.exports = format(
  74. header.trimEnd(),
  75. 'contract SlotDerivationTest is Test, SymTest {',
  76. 'using SlotDerivation for bytes32;',
  77. '',
  78. array,
  79. TYPES.flatMap(type =>
  80. [].concat(
  81. type,
  82. (type.variants ?? []).map(variant => ({
  83. type: variant,
  84. name: capitalize(variant),
  85. isValueType: type.isValueType,
  86. })),
  87. ),
  88. ).map(type => (type.isValueType ? mapping(type) : boundedMapping(type))),
  89. '}',
  90. );