@@ -35,7 +35,14 @@ contract SortitionTree {
3535 // level6 256k
3636 // level7 2M
3737 uint256 internal root;
38+
39+ // A mapping from layer => (index => branch). For example, to access the 6th
40+ // (0-index) branch in the 2nd layer (right below the root node), call
41+ // branches[2][6]. Mappings are used in place of arrays for efficiency.
3842 mapping (uint256 => mapping (uint256 => uint256 )) internal branches;
43+
44+ // A mapping from index => leaf, acting as an array. For example, to access
45+ // the 42nd (0-index) leaf, call leaves[42].
3946 mapping (uint256 => uint256 ) internal leaves;
4047
4148 // the flagged (see setFlag() and unsetFlag() in Position.sol) positions
@@ -44,6 +51,7 @@ contract SortitionTree {
4451
4552 // the leaf after the rightmost occupied leaf of each stack
4653 uint256 internal rightmostLeaf;
54+
4755 // the empty leaves in each stack
4856 // between 0 and the rightmost occupied leaf
4957 uint256 [] internal emptyLeaves;
@@ -52,6 +60,7 @@ contract SortitionTree {
5260 // which is allocated when they first join the pool
5361 // and remains unchanged even if they leave and rejoin the pool.
5462 mapping (address => uint32 ) internal operatorID;
63+
5564 // The idAddress array records the address corresponding to each ID number.
5665 // The ID number 0 is initialized with a zero address and is not used.
5766 address [] internal idAddress;
@@ -62,22 +71,28 @@ contract SortitionTree {
6271 idAddress.push ();
6372 }
6473
65- // Return the ID number of the given operator address.
66- // An ID number of 0 means the operator has not been allocated an ID number yet.
74+ /// @notice Return the ID number of the given operator address. An ID number
75+ /// of 0 means the operator has not been allocated an ID number yet.
76+ /// @param operator Address of the operator.
77+ /// @return the ID number of the given operator address
6778 function getOperatorID (address operator ) public view returns (uint32 ) {
6879 return operatorID[operator];
6980 }
7081
71- // Get the operator address corresponding to the given ID number.
72- // An empty address means the ID number has not been allocated yet.
82+ /// @notice Get the operator address corresponding to the given ID number. An
83+ /// empty address means the ID number has not been allocated yet.
84+ /// @param id ID of the operator
85+ /// @return the address of the operator
7386 function getIDOperator (uint32 id ) public view returns (address ) {
7487 return idAddress.length > id ? idAddress[id] : address (0 );
7588 }
7689
77- // Gets the operator addresses corresponding to the given ID numbers.
78- // An empty address means the ID number has not been allocated yet.
79- // This function works just like getIDOperator except that it allows to fetch
80- // operator addresses for multiple IDs in one call.
90+ /// @notice Gets the operator addresses corresponding to the given ID
91+ /// numbers. An empty address means the ID number has not been allocated yet.
92+ /// This function works just like getIDOperator except that it allows to fetch
93+ /// operator addresses for multiple IDs in one call.
94+ /// @param ids the array of the operator ids
95+ /// @return an array of the associated operator addresses
8196 function getIDOperators (uint32 [] calldata ids )
8297 public
8398 view
@@ -93,12 +108,15 @@ contract SortitionTree {
93108 return operators;
94109 }
95110
96- // checks if operator is already registered in the pool
111+ /// @notice Checks if operator is already registered in the pool.
112+ /// @param operator the address of the operator
113+ /// @return whether or not the operator is already registered in the pool
97114 function isOperatorRegistered (address operator ) public view returns (bool ) {
98115 return getFlaggedLeafPosition (operator) != 0 ;
99116 }
100117
101- // Sum the number of operators in each trunk
118+ /// @notice Sum the number of operators in each trunk.
119+ /// @return the number of operators in the pool
102120 function operatorsInPool () public view returns (uint256 ) {
103121 // Get the number of leaves that might be occupied;
104122 // if `rightmostLeaf` equals `firstLeaf()` the tree must be empty,
@@ -112,12 +130,16 @@ contract SortitionTree {
112130 return (nPossiblyUsedLeaves - nEmptyLeaves);
113131 }
114132
133+ /// @notice Convenience method to return the total weight of the pool
134+ /// @return the total weight of the pool
115135 function totalWeight () public view returns (uint256 ) {
116136 return root.sumWeight ();
117137 }
118138
119- // Give the operator a new ID number
120- // Does not check if the operator already has an ID number
139+ /// @notice Give the operator a new ID number.
140+ /// Does not check if the operator already has an ID number.
141+ /// @param operator the address of the operator
142+ /// @return a new ID for that operator
121143 function allocateOperatorID (address operator ) internal returns (uint256 ) {
122144 uint256 id = idAddress.length ;
123145
@@ -128,36 +150,51 @@ contract SortitionTree {
128150 return id;
129151 }
130152
153+ /// @notice Inserts an operator into the sortition pool
154+ /// @param operator the address of an operator to insert
155+ /// @param weight how much weight that operator has in the pool
131156 function _insertOperator (address operator , uint256 weight ) internal {
132157 require (
133158 ! isOperatorRegistered (operator),
134159 "Operator is already registered in the pool "
135160 );
136161
162+ // Fetch the operator's ID, and if they don't have one, allocate them one.
137163 uint256 id = getOperatorID (operator);
138164 if (id == 0 ) {
139165 id = allocateOperatorID (operator);
140166 }
141167
168+ // Determine which leaf to insert them into
142169 uint256 position = getEmptyLeafPosition ();
143170 // Record the block the operator was inserted in
144171 uint256 theLeaf = Leaf.make (operator, block .number , id);
145172
173+ // Update the leaf, and propagate the weight changes all the way up to the
174+ // root.
146175 root = setLeaf (position, theLeaf, weight, root);
147176
148177 // Without position flags,
149178 // the position 0x000000 would be treated as empty
150179 flaggedLeafPosition[operator] = position.setFlag ();
151180 }
152181
182+ /// @notice Remove an operator (and their weight) from the pool.
183+ /// @param operator the address of the operator to remove
153184 function _removeOperator (address operator ) internal {
154185 uint256 flaggedPosition = getFlaggedLeafPosition (operator);
155186 require (flaggedPosition != 0 , "Operator is not registered in the pool " );
156187 uint256 unflaggedPosition = flaggedPosition.unsetFlag ();
188+
189+ // Update the leaf, and propagate the weight changes all the way up to the
190+ // root.
157191 root = removeLeaf (unflaggedPosition, root);
158192 removeLeafPositionRecord (operator);
159193 }
160194
195+ /// @notice Update an operator's weight in the pool.
196+ /// @param operator the address of the operator to update
197+ /// @param weight the new weight
161198 function updateOperator (address operator , uint256 weight ) internal {
162199 require (
163200 isOperatorRegistered (operator),
@@ -169,19 +206,28 @@ contract SortitionTree {
169206 root = updateLeaf (unflaggedPosition, weight, root);
170207 }
171208
209+ /// @notice Helper method to remove a leaf position record for an operator.
210+ /// @param operator the address of the operator to remove the record for
172211 function removeLeafPositionRecord (address operator ) internal {
173212 flaggedLeafPosition[operator] = 0 ;
174213 }
175214
215+ /// @notice Removes the data and weight from a particular leaf.
216+ /// @param position the leaf index to remove
217+ /// @param _root the root node containing the leaf
218+ /// @return the updated root node
176219 function removeLeaf (uint256 position , uint256 _root )
177220 internal
178221 returns (uint256 )
179222 {
180223 uint256 rightmostSubOne = rightmostLeaf - 1 ;
181224 bool isRightmost = position == rightmostSubOne;
182225
226+ // Clears out the data in the leaf node, and then propagates the weight
227+ // changes all the way up to the root.
183228 uint256 newRoot = setLeaf (position, 0 , 0 , _root);
184229
230+ // Infer if need to fall back on emptyLeaves yet
185231 if (isRightmost) {
186232 rightmostLeaf = rightmostSubOne;
187233 } else {
@@ -190,6 +236,11 @@ contract SortitionTree {
190236 return newRoot;
191237 }
192238
239+ /// @notice Updates the tree to give a particular leaf a new weight.
240+ /// @param position the index of the leaf to update
241+ /// @param weight the new weight
242+ /// @param _root the root node containing the leaf
243+ /// @return the updated root node
193244 function updateLeaf (
194245 uint256 position ,
195246 uint256 weight ,
@@ -202,6 +253,13 @@ contract SortitionTree {
202253 }
203254 }
204255
256+ /// @notice Places a leaf into a particular position, with a given weight and
257+ /// propagates that change.
258+ /// @param position the index to place the leaf in
259+ /// @param theLeaf the new leaf to place in the position
260+ /// @param leafWeight the weight of the leaf
261+ /// @param _root the root containing the new leaf
262+ /// @return the updated root node
205263 function setLeaf (
206264 uint256 position ,
207265 uint256 theLeaf ,
@@ -214,6 +272,12 @@ contract SortitionTree {
214272 return (updateTree (position, leafWeight, _root));
215273 }
216274
275+ /// @notice Propagates a weight change at a position through the tree,
276+ /// eventually returning the updated root.
277+ /// @param position the index of leaf to update
278+ /// @param weight the new weight of the leaf
279+ /// @param _root the root node containing the leaf
280+ /// @return the updated root node
217281 function updateTree (
218282 uint256 position ,
219283 uint256 weight ,
@@ -240,6 +304,10 @@ contract SortitionTree {
240304 return _root.setSlot (childSlot, nodeWeight);
241305 }
242306
307+ /// @notice Retrieves the next available empty leaf position. Tries to fill
308+ /// left to right first, ignoring leaf removals, and then fills
309+ /// most-recent-removals first.
310+ /// @return the position of the empty leaf
243311 function getEmptyLeafPosition () internal returns (uint256 ) {
244312 uint256 rLeaf = rightmostLeaf;
245313 bool spaceOnRight = (rLeaf + 1 ) < POOL_CAPACITY;
@@ -255,6 +323,9 @@ contract SortitionTree {
255323 }
256324 }
257325
326+ /// @notice Gets the flagged leaf position for an operator.
327+ /// @param operator the address of the operator
328+ /// @return the leaf position of that operator
258329 function getFlaggedLeafPosition (address operator )
259330 internal
260331 view
@@ -263,13 +334,24 @@ contract SortitionTree {
263334 return flaggedLeafPosition[operator];
264335 }
265336
337+ /// @notice Gets the weight of a leaf at a particular position.
338+ /// @param position the index of the leaf
339+ /// @return the weight of the leaf at that position
266340 function getLeafWeight (uint256 position ) internal view returns (uint256 ) {
267341 uint256 slot = position.slot ();
268342 uint256 parent = position.parent ();
343+
344+ // A leaf's weight information is stored a 32-bit slot in the branch layer
345+ // directly above the leaf layer. To access it, we calculate that slot and
346+ // parent position, and always know the hard-coded layer index.
269347 uint256 node = branches[LEVELS][parent];
270348 return node.getSlot (slot);
271349 }
272350
351+ /// @notice Picks a leaf given a random index.
352+ /// @param index a number in `[0, _root.totalWeight())` used to decide
353+ /// between leaves
354+ /// @param _root the root of the tree
273355 function pickWeightedLeaf (uint256 index , uint256 _root )
274356 internal
275357 view
0 commit comments