github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/dispute/lib/LibPosition.t.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity ^0.8.15;
     3  
     4  import { Test } from "forge-std/Test.sol";
     5  import { LibPosition } from "src/dispute/lib/LibPosition.sol";
     6  import "src/libraries/DisputeTypes.sol";
     7  
     8  /// @notice Tests for `LibPosition`
     9  contract LibPosition_Test is Test {
    10      /// @dev Assumes a MAX depth of 63 for the Position type. Any greater depth can cause overflows.
    11      /// @dev At the lowest level of the tree, this allows for 2 ** 63 leaves. In reality, the max game depth
    12      ///      will likely be much lower.
    13      uint8 internal constant MAX_DEPTH = 63;
    14      /// @dev Arbitrary split depth around half way down the tree.
    15      uint8 internal constant SPLIT_DEPTH = 30;
    16  
    17      function boundIndexAtDepth(uint8 _depth, uint64 _indexAtDepth) internal pure returns (uint64) {
    18          // Index at depth bound: [0, 2 ** _depth-1]
    19          if (_depth > 0) {
    20              return uint64(bound(_indexAtDepth, 0, 2 ** (_depth - 1)));
    21          } else {
    22              return 0;
    23          }
    24      }
    25  
    26      /// @notice Tests that the `depth` function correctly shifts out the `depth` from a packed `Position` type.
    27      function testFuzz_depth_correctness_succeeds(uint8 _depth, uint64 _indexAtDepth) public {
    28          _depth = uint8(bound(_depth, 0, MAX_DEPTH));
    29          _indexAtDepth = boundIndexAtDepth(_depth, _indexAtDepth);
    30          Position position = LibPosition.wrap(_depth, _indexAtDepth);
    31          assertEq(position.depth(), _depth);
    32      }
    33  
    34      /// @notice Tests that the `indexAtDepth` function correctly shifts out the `indexAtDepth` from a packed `Position`
    35      /// type.
    36      function testFuzz_indexAtDepth_correctness_succeeds(uint8 _depth, uint64 _indexAtDepth) public {
    37          _depth = uint8(bound(_depth, 0, MAX_DEPTH));
    38          _indexAtDepth = boundIndexAtDepth(_depth, _indexAtDepth);
    39          Position position = LibPosition.wrap(_depth, _indexAtDepth);
    40          assertEq(position.indexAtDepth(), _indexAtDepth);
    41      }
    42  
    43      /// @notice Tests that the `left` function correctly computes the position of the left child.
    44      function testFuzz_left_correctness_succeeds(uint8 _depth, uint64 _indexAtDepth) public {
    45          _depth = uint8(bound(_depth, 0, MAX_DEPTH));
    46          _indexAtDepth = boundIndexAtDepth(_depth, _indexAtDepth);
    47  
    48          Position position = LibPosition.wrap(_depth, _indexAtDepth);
    49          Position left = position.left();
    50  
    51          assertEq(left.depth(), uint64(_depth) + 1);
    52          assertEq(left.indexAtDepth(), _indexAtDepth * 2);
    53      }
    54  
    55      /// @notice Tests that the `right` function correctly computes the position of the right child.
    56      function testFuzz_right_correctness_succeeds(uint8 _depth, uint64 _indexAtDepth) public {
    57          // Depth bound: [0, 63]
    58          _depth = uint8(bound(_depth, 0, MAX_DEPTH));
    59          _indexAtDepth = boundIndexAtDepth(_depth, _indexAtDepth);
    60  
    61          Position position = LibPosition.wrap(_depth, _indexAtDepth);
    62          Position right = position.right();
    63  
    64          assertEq(right.depth(), _depth + 1);
    65          assertEq(right.indexAtDepth(), _indexAtDepth * 2 + 1);
    66      }
    67  
    68      /// @notice Tests that the `parent` function correctly computes the position of the parent.
    69      function testFuzz_parent_correctness_succeeds(uint8 _depth, uint64 _indexAtDepth) public {
    70          _depth = uint8(bound(_depth, 1, MAX_DEPTH));
    71          _indexAtDepth = boundIndexAtDepth(_depth, _indexAtDepth);
    72  
    73          Position position = LibPosition.wrap(_depth, _indexAtDepth);
    74          Position parent = position.parent();
    75  
    76          assertEq(parent.depth(), _depth - 1);
    77          assertEq(parent.indexAtDepth(), _indexAtDepth / 2);
    78      }
    79  
    80      /// @notice Tests that the `traceAncestor` function correctly computes the position of the
    81      ///         highest ancestor that commits to the same trace index.
    82      function testFuzz_traceAncestor_correctness_succeeds(uint8 _depth, uint64 _indexAtDepth) public {
    83          _depth = uint8(bound(_depth, 1, MAX_DEPTH));
    84          _indexAtDepth = boundIndexAtDepth(_depth, _indexAtDepth);
    85  
    86          Position position = LibPosition.wrap(_depth, _indexAtDepth);
    87          Position ancestor = position.traceAncestor();
    88          Position loopAncestor = position;
    89          while (loopAncestor.parent().traceIndex(MAX_DEPTH) == position.traceIndex(MAX_DEPTH)) {
    90              loopAncestor = loopAncestor.parent();
    91          }
    92  
    93          assertEq(Position.unwrap(ancestor), Position.unwrap(loopAncestor));
    94      }
    95  
    96      /// @notice Tests that the `traceAncestorBounded` function correctly computes the position of the
    97      ///         highest ancestor (below `SPLIT_DEPTH`) that commits to the same trace index.
    98      function testFuzz_traceAncestorBounded_correctness_succeeds(uint8 _depth, uint64 _indexAtDepth) public {
    99          _depth = uint8(bound(_depth, SPLIT_DEPTH + 1, MAX_DEPTH));
   100          _indexAtDepth = boundIndexAtDepth(_depth, _indexAtDepth);
   101  
   102          Position position = LibPosition.wrap(_depth, _indexAtDepth);
   103          Position ancestor = position.traceAncestorBounded(SPLIT_DEPTH);
   104          Position loopAncestor = position;
   105  
   106          // Stop at 1 below the split depth.
   107          while (
   108              loopAncestor.parent().traceIndex(MAX_DEPTH) == position.traceIndex(MAX_DEPTH)
   109                  && loopAncestor.depth() != SPLIT_DEPTH + 1
   110          ) {
   111              loopAncestor = loopAncestor.parent();
   112          }
   113  
   114          assertEq(Position.unwrap(ancestor), Position.unwrap(loopAncestor));
   115      }
   116  
   117      /// @notice Tests that the `rightIndex` function correctly computes the deepest, right most index relative
   118      ///         to a given position.
   119      function testFuzz_rightIndex_correctness_succeeds(uint64 _maxDepth, uint8 _depth, uint64 _indexAtDepth) public {
   120          // Max depth bound: [1, 63]
   121          // The max game depth MUST be at least 1.
   122          _maxDepth = uint8(bound(_maxDepth, 1, MAX_DEPTH));
   123          // Depth bound: [0, _maxDepth]
   124          _depth = uint8(bound(_depth, 0, _maxDepth));
   125          _indexAtDepth = boundIndexAtDepth(_depth, _indexAtDepth);
   126  
   127          Position position = LibPosition.wrap(_depth, _indexAtDepth);
   128          Position rightIndex = position.rightIndex(_maxDepth);
   129  
   130          // Find the deepest, rightmost index in Solidity rather than Yul
   131          for (uint256 i = _depth; i < _maxDepth; ++i) {
   132              position = position.right();
   133          }
   134  
   135          assertEq(Position.unwrap(rightIndex), Position.unwrap(position));
   136      }
   137  
   138      /// @notice Tests that the `attack` function correctly computes the position of the attack relative to
   139      ///         a given position.
   140      /// @dev `attack` is an alias for `left`, but we test it separately for completeness.
   141      function testFuzz_attack_correctness_succeeds(uint8 _depth, uint64 _indexAtDepth) public {
   142          // Depth bound: [0, 63]
   143          _depth = uint8(bound(_depth, 0, MAX_DEPTH));
   144          _indexAtDepth = boundIndexAtDepth(_depth, _indexAtDepth);
   145  
   146          Position position = LibPosition.wrap(_depth, _indexAtDepth);
   147          Position attack = position.move(true);
   148  
   149          assertEq(attack.depth(), _depth + 1);
   150          assertEq(attack.indexAtDepth(), _indexAtDepth * 2);
   151      }
   152  
   153      /// @notice Tests that the `defend` function correctly computes the position of the defense relative to
   154      ///         a given position.
   155      /// @dev A defense can only be given if the position does not belong to the root claim, hence the bound of [1, 127]
   156      ///      on the depth.
   157      function testFuzz_defend_correctness_succeeds(uint8 _depth, uint64 _indexAtDepth) public {
   158          // Depth bound: [1, 63]
   159          _depth = uint8(bound(_depth, 1, MAX_DEPTH));
   160          _indexAtDepth = boundIndexAtDepth(_depth, _indexAtDepth);
   161  
   162          Position position = LibPosition.wrap(_depth, _indexAtDepth);
   163          Position defend = position.move(false);
   164  
   165          assertEq(defend.depth(), _depth + 1);
   166          assertEq(defend.indexAtDepth(), ((_indexAtDepth / 2) * 2 + 1) * 2);
   167      }
   168  
   169      /// @notice A static unit test for the correctness of all gindicies, (depth, index) combos,
   170      ///         and the trace index in a tree of max depth = 4.
   171      function test_pos_correctness_succeeds() public {
   172          uint256 maxDepth = 4;
   173  
   174          Position p = LibPosition.wrap(0, 0);
   175          assertEq(Position.unwrap(p), 1); // gindex = 1
   176          assertEq(p.depth(), 0); // depth = 0
   177          assertEq(p.indexAtDepth(), 0); // indexAtDepth = 0
   178          Position r = p.rightIndex(maxDepth);
   179          assertEq(Position.unwrap(r), 31); // right gindex = 31
   180          assertEq(r.indexAtDepth(), 15); // trace index = 15
   181  
   182          p = LibPosition.wrap(1, 0);
   183          assertEq(Position.unwrap(p), 2); // gindex = 2
   184          assertEq(p.depth(), 1); // depth = 1
   185          assertEq(p.indexAtDepth(), 0); // indexAtDepth = 0
   186          r = p.rightIndex(maxDepth);
   187          assertEq(Position.unwrap(r), 23); // right gindex = 23
   188          assertEq(r.indexAtDepth(), 7); // trace index = 7
   189  
   190          p = LibPosition.wrap(1, 1);
   191          assertEq(Position.unwrap(p), 3); // gindex = 3
   192          assertEq(p.depth(), 1); // depth = 1
   193          assertEq(p.indexAtDepth(), 1); // indexAtDepth = 1
   194          r = p.rightIndex(maxDepth);
   195          assertEq(Position.unwrap(r), 31); // right gindex = 31
   196          assertEq(r.indexAtDepth(), 15); // trace index = 15
   197  
   198          p = LibPosition.wrap(2, 0);
   199          assertEq(Position.unwrap(p), 4); // gindex = 4
   200          assertEq(p.depth(), 2); // depth = 2
   201          assertEq(p.indexAtDepth(), 0); // indexAtDepth = 0
   202          r = p.rightIndex(maxDepth);
   203          assertEq(Position.unwrap(r), 19); // right gindex = 19
   204          assertEq(r.indexAtDepth(), 3); // trace index = 3
   205  
   206          p = LibPosition.wrap(2, 1);
   207          assertEq(Position.unwrap(p), 5); // gindex = 5
   208          assertEq(p.depth(), 2); // depth = 2
   209          assertEq(p.indexAtDepth(), 1); // indexAtDepth = 1
   210          r = p.rightIndex(maxDepth);
   211          assertEq(Position.unwrap(r), 23); // right gindex = 23
   212          assertEq(r.indexAtDepth(), 7); // trace index = 7
   213  
   214          p = LibPosition.wrap(2, 2);
   215          assertEq(Position.unwrap(p), 6); // gindex = 6
   216          assertEq(p.depth(), 2); // depth = 2
   217          assertEq(p.indexAtDepth(), 2); // indexAtDepth = 2
   218          r = p.rightIndex(maxDepth);
   219          assertEq(Position.unwrap(r), 27); // right gindex = 27
   220          assertEq(r.indexAtDepth(), 11); // trace index = 11
   221  
   222          p = LibPosition.wrap(2, 3);
   223          assertEq(Position.unwrap(p), 7); // gindex = 7
   224          assertEq(p.depth(), 2); // depth = 2
   225          assertEq(p.indexAtDepth(), 3); // indexAtDepth = 3
   226          r = p.rightIndex(maxDepth);
   227          assertEq(Position.unwrap(r), 31); // right gindex = 31
   228          assertEq(r.indexAtDepth(), 15); // trace index = 15
   229  
   230          p = LibPosition.wrap(3, 0);
   231          assertEq(Position.unwrap(p), 8); // gindex = 8
   232          assertEq(p.depth(), 3); // depth = 3
   233          assertEq(p.indexAtDepth(), 0); // indexAtDepth = 0
   234          r = p.rightIndex(maxDepth);
   235          assertEq(Position.unwrap(r), 17); // right gindex = 17
   236          assertEq(r.indexAtDepth(), 1); // trace index = 1
   237  
   238          p = LibPosition.wrap(3, 1);
   239          assertEq(Position.unwrap(p), 9); // gindex = 9
   240          assertEq(p.depth(), 3); // depth = 3
   241          assertEq(p.indexAtDepth(), 1); // indexAtDepth = 1
   242          r = p.rightIndex(maxDepth);
   243          assertEq(Position.unwrap(r), 19); // right gindex = 19
   244          assertEq(r.indexAtDepth(), 3); // trace index = 3
   245  
   246          p = LibPosition.wrap(3, 2);
   247          assertEq(Position.unwrap(p), 10); // gindex = 10
   248          assertEq(p.depth(), 3); // depth = 3
   249          assertEq(p.indexAtDepth(), 2); // indexAtDepth = 2
   250          r = p.rightIndex(maxDepth);
   251          assertEq(Position.unwrap(r), 21); // right gindex = 21
   252          assertEq(r.indexAtDepth(), 5); // trace index = 5
   253  
   254          p = LibPosition.wrap(3, 3);
   255          assertEq(Position.unwrap(p), 11); // gindex = 11
   256          assertEq(p.depth(), 3); // depth = 3
   257          assertEq(p.indexAtDepth(), 3); // indexAtDepth = 3
   258          r = p.rightIndex(maxDepth);
   259          assertEq(Position.unwrap(r), 23); // right gindex = 23
   260          assertEq(r.indexAtDepth(), 7); // trace index = 7
   261  
   262          p = LibPosition.wrap(3, 4);
   263          assertEq(Position.unwrap(p), 12); // gindex = 12
   264          assertEq(p.depth(), 3); // depth = 3
   265          assertEq(p.indexAtDepth(), 4); // indexAtDepth = 4
   266          r = p.rightIndex(maxDepth);
   267          assertEq(Position.unwrap(r), 25); // right gindex = 25
   268          assertEq(r.indexAtDepth(), 9); // trace index = 9
   269  
   270          p = LibPosition.wrap(3, 5);
   271          assertEq(Position.unwrap(p), 13); // gindex = 13
   272          assertEq(p.depth(), 3); // depth = 3
   273          assertEq(p.indexAtDepth(), 5); // indexAtDepth = 5
   274          r = p.rightIndex(maxDepth);
   275          assertEq(Position.unwrap(r), 27); // right gindex = 27
   276          assertEq(r.indexAtDepth(), 11); // trace index = 11
   277  
   278          p = LibPosition.wrap(3, 6);
   279          assertEq(Position.unwrap(p), 14); // gindex = 14
   280          assertEq(p.depth(), 3); // depth = 3
   281          assertEq(p.indexAtDepth(), 6); // indexAtDepth = 6
   282          r = p.rightIndex(maxDepth);
   283          assertEq(Position.unwrap(r), 29); // right gindex = 29
   284          assertEq(r.indexAtDepth(), 13); // trace index = 13
   285  
   286          p = LibPosition.wrap(3, 7);
   287          assertEq(Position.unwrap(p), 15); // gindex = 15
   288          assertEq(p.depth(), 3); // depth = 3
   289          assertEq(p.indexAtDepth(), 7); // indexAtDepth = 7
   290          r = p.rightIndex(maxDepth);
   291          assertEq(Position.unwrap(r), 31); // right gindex = 31
   292          assertEq(r.indexAtDepth(), 15); // trace index = 15
   293  
   294          p = LibPosition.wrap(4, 0);
   295          assertEq(Position.unwrap(p), 16); // gindex = 16
   296          assertEq(p.depth(), 4); // depth = 4
   297          assertEq(p.indexAtDepth(), 0); // indexAtDepth = 0
   298          r = p.rightIndex(maxDepth);
   299          assertEq(Position.unwrap(r), 16); // right gindex = 16
   300          assertEq(r.indexAtDepth(), 0); // trace index = 0
   301  
   302          p = LibPosition.wrap(4, 1);
   303          assertEq(Position.unwrap(p), 17); // gindex = 17
   304          assertEq(p.depth(), 4); // depth = 4
   305          assertEq(p.indexAtDepth(), 1); // indexAtDepth = 1
   306          r = p.rightIndex(maxDepth);
   307          assertEq(Position.unwrap(r), 17); // right gindex = 17
   308          assertEq(r.indexAtDepth(), 1); // trace index = 1
   309  
   310          p = LibPosition.wrap(4, 2);
   311          assertEq(Position.unwrap(p), 18); // gindex = 18
   312          assertEq(p.depth(), 4); // depth = 4
   313          assertEq(p.indexAtDepth(), 2); // indexAtDepth = 2
   314          r = p.rightIndex(maxDepth);
   315          assertEq(Position.unwrap(r), 18); // right gindex = 18
   316          assertEq(r.indexAtDepth(), 2); // trace index = 2
   317  
   318          p = LibPosition.wrap(4, 3);
   319          assertEq(Position.unwrap(p), 19); // gindex = 19
   320          assertEq(p.depth(), 4); // depth = 4
   321          assertEq(p.indexAtDepth(), 3); // indexAtDepth = 3
   322          r = p.rightIndex(maxDepth);
   323          assertEq(Position.unwrap(r), 19); // right gindex = 19
   324          assertEq(r.indexAtDepth(), 3); // trace index = 3
   325  
   326          p = LibPosition.wrap(4, 4);
   327          assertEq(Position.unwrap(p), 20); // gindex = 20
   328          assertEq(p.depth(), 4); // depth = 4
   329          assertEq(p.indexAtDepth(), 4); // indexAtDepth = 4
   330          r = p.rightIndex(maxDepth);
   331          assertEq(Position.unwrap(r), 20); // right gindex = 20
   332          assertEq(r.indexAtDepth(), 4); // trace index = 4
   333  
   334          p = LibPosition.wrap(4, 5);
   335          assertEq(Position.unwrap(p), 21); // gindex = 21
   336          assertEq(p.depth(), 4); // depth = 4
   337          assertEq(p.indexAtDepth(), 5); // indexAtDepth = 5
   338          r = p.rightIndex(maxDepth);
   339          assertEq(Position.unwrap(r), 21); // right gindex = 21
   340          assertEq(r.indexAtDepth(), 5); // trace index = 5
   341  
   342          p = LibPosition.wrap(4, 6);
   343          assertEq(Position.unwrap(p), 22); // gindex = 22
   344          assertEq(p.depth(), 4); // depth = 4
   345          assertEq(p.indexAtDepth(), 6); // indexAtDepth = 6
   346          r = p.rightIndex(maxDepth);
   347          assertEq(Position.unwrap(r), 22); // right gindex = 22
   348          assertEq(r.indexAtDepth(), 6); // trace index = 6
   349  
   350          p = LibPosition.wrap(4, 7);
   351          assertEq(Position.unwrap(p), 23); // gindex = 23
   352          assertEq(p.depth(), 4); // depth = 4
   353          assertEq(p.indexAtDepth(), 7); // indexAtDepth = 7
   354          r = p.rightIndex(maxDepth);
   355          assertEq(Position.unwrap(r), 23); // right gindex = 23
   356          assertEq(r.indexAtDepth(), 7); // trace index = 7
   357  
   358          p = LibPosition.wrap(4, 8);
   359          assertEq(Position.unwrap(p), 24); // gindex = 24
   360          assertEq(p.depth(), 4); // depth = 4
   361          assertEq(p.indexAtDepth(), 8); // indexAtDepth = 8
   362          r = p.rightIndex(maxDepth);
   363          assertEq(Position.unwrap(r), 24); // right gindex = 24
   364          assertEq(r.indexAtDepth(), 8); // trace index = 8
   365  
   366          p = LibPosition.wrap(4, 9);
   367          assertEq(Position.unwrap(p), 25); // gindex = 25
   368          assertEq(p.depth(), 4); // depth = 4
   369          assertEq(p.indexAtDepth(), 9); // indexAtDepth = 9
   370          r = p.rightIndex(maxDepth);
   371          assertEq(Position.unwrap(r), 25); // right gindex = 25
   372          assertEq(r.indexAtDepth(), 9); // trace index = 9
   373  
   374          p = LibPosition.wrap(4, 10);
   375          assertEq(Position.unwrap(p), 26); // gindex = 26
   376          assertEq(p.depth(), 4); // depth = 4
   377          assertEq(p.indexAtDepth(), 10); // indexAtDepth = 10
   378          r = p.rightIndex(maxDepth);
   379          assertEq(Position.unwrap(r), 26); // right gindex = 26
   380          assertEq(r.indexAtDepth(), 10); // trace index = 10
   381  
   382          p = LibPosition.wrap(4, 11);
   383          assertEq(Position.unwrap(p), 27); // gindex = 27
   384          assertEq(p.depth(), 4); // depth = 4
   385          assertEq(p.indexAtDepth(), 11); // indexAtDepth = 11
   386          r = p.rightIndex(maxDepth);
   387          assertEq(Position.unwrap(r), 27); // right gindex = 27
   388          assertEq(r.indexAtDepth(), 11); // trace index = 11
   389  
   390          p = LibPosition.wrap(4, 12);
   391          assertEq(Position.unwrap(p), 28); // gindex = 28
   392          assertEq(p.depth(), 4); // depth = 4
   393          assertEq(p.indexAtDepth(), 12); // indexAtDepth = 12
   394          r = p.rightIndex(maxDepth);
   395          assertEq(Position.unwrap(r), 28); // right gindex = 28
   396          assertEq(r.indexAtDepth(), 12); // trace index = 12
   397  
   398          p = LibPosition.wrap(4, 13);
   399          assertEq(Position.unwrap(p), 29); // gindex = 29
   400          assertEq(p.depth(), 4); // depth = 4
   401          assertEq(p.indexAtDepth(), 13); // indexAtDepth = 13
   402          r = p.rightIndex(maxDepth);
   403          assertEq(Position.unwrap(r), 29); // right gindex = 29
   404          assertEq(r.indexAtDepth(), 13); // trace index = 13
   405  
   406          p = LibPosition.wrap(4, 14);
   407          assertEq(Position.unwrap(p), 30); // gindex = 30
   408          assertEq(p.depth(), 4); // depth = 4
   409          assertEq(p.indexAtDepth(), 14); // indexAtDepth = 14
   410          r = p.rightIndex(maxDepth);
   411          assertEq(Position.unwrap(r), 30); // right gindex = 30
   412          assertEq(r.indexAtDepth(), 14); // trace index = 14
   413  
   414          p = LibPosition.wrap(4, 15);
   415          assertEq(Position.unwrap(p), 31); // gindex = 31
   416          assertEq(p.depth(), 4); // depth = 4
   417          assertEq(p.indexAtDepth(), 15); // indexAtDepth = 15
   418          r = p.rightIndex(maxDepth);
   419          assertEq(Position.unwrap(r), 31); // right gindex = 31
   420          assertEq(r.indexAtDepth(), 15); // trace index = 15
   421      }
   422  }