@@ -19,7 +19,18 @@ contract SortitionTree {
1919 // level6 256k
2020 // level7 2M
2121 uint256 internal root;
22+
23+ // A 2-index mapping from layer => (index (0-index) => branch). For example,
24+ // to access the 6th branch in the 2nd layer (right below the root node; the
25+ // first branch layer), call branches[2][5]. Mappings are used in place of
26+ // arrays for efficiency. The root is the first layer, the branches occupy
27+ // layers 2 through 7, and layer 8 is for the leaves. Following this
28+ // convention, the first index in `branches` is `2`, and the last index is
29+ // `7`.
2230 mapping (uint256 => mapping (uint256 => uint256 )) internal branches;
31+
32+ // A 0-index mapping from index => leaf, acting as an array. For example, to
33+ // access the 42nd leaf, call leaves[41].
2334 mapping (uint256 => uint256 ) internal leaves;
2435
2536 // the flagged (see setFlag() and unsetFlag() in Position.sol) positions
@@ -28,6 +39,7 @@ contract SortitionTree {
2839
2940 // the leaf after the rightmost occupied leaf of each stack
3041 uint256 internal rightmostLeaf;
42+
3143 // the empty leaves in each stack
3244 // between 0 and the rightmost occupied leaf
3345 uint256 [] internal emptyLeaves;
@@ -36,6 +48,7 @@ contract SortitionTree {
3648 // which is allocated when they first join the pool
3749 // and remains unchanged even if they leave and rejoin the pool.
3850 mapping (address => uint32 ) internal operatorID;
51+
3952 // The idAddress array records the address corresponding to each ID number.
4053 // The ID number 0 is initialized with a zero address and is not used.
4154 address [] internal idAddress;
@@ -46,22 +59,28 @@ contract SortitionTree {
4659 idAddress.push ();
4760 }
4861
49- // Return the ID number of the given operator address.
50- // An ID number of 0 means the operator has not been allocated an ID number yet.
62+ /// @notice Return the ID number of the given operator address. An ID number
63+ /// of 0 means the operator has not been allocated an ID number yet.
64+ /// @param operator Address of the operator.
65+ /// @return the ID number of the given operator address
5166 function getOperatorID (address operator ) public view returns (uint32 ) {
5267 return operatorID[operator];
5368 }
5469
55- // Get the operator address corresponding to the given ID number.
56- // An empty address means the ID number has not been allocated yet.
70+ /// @notice Get the operator address corresponding to the given ID number. A
71+ /// zero address means the ID number has not been allocated yet.
72+ /// @param id ID of the operator
73+ /// @return the address of the operator
5774 function getIDOperator (uint32 id ) public view returns (address ) {
5875 return idAddress.length > id ? idAddress[id] : address (0 );
5976 }
6077
61- // Gets the operator addresses corresponding to the given ID numbers.
62- // An empty address means the ID number has not been allocated yet.
63- // This function works just like getIDOperator except that it allows to fetch
64- // operator addresses for multiple IDs in one call.
78+ /// @notice Gets the operator addresses corresponding to the given ID
79+ /// numbers. A zero address means the ID number has not been allocated yet.
80+ /// This function works just like getIDOperator except that it allows to fetch
81+ /// operator addresses for multiple IDs in one call.
82+ /// @param ids the array of the operator ids
83+ /// @return an array of the associated operator addresses
6584 function getIDOperators (uint32 [] calldata ids )
6685 public
6786 view
@@ -77,12 +96,15 @@ contract SortitionTree {
7796 return operators;
7897 }
7998
80- // checks if operator is already registered in the pool
99+ /// @notice Checks if operator is already registered in the pool.
100+ /// @param operator the address of the operator
101+ /// @return whether or not the operator is already registered in the pool
81102 function isOperatorRegistered (address operator ) public view returns (bool ) {
82103 return getFlaggedLeafPosition (operator) != 0 ;
83104 }
84105
85- // Sum the number of operators in each trunk
106+ /// @notice Sum the number of operators in each trunk.
107+ /// @return the number of operators in the pool
86108 function operatorsInPool () public view returns (uint256 ) {
87109 // Get the number of leaves that might be occupied;
88110 // if `rightmostLeaf` equals `firstLeaf()` the tree must be empty,
@@ -96,12 +118,16 @@ contract SortitionTree {
96118 return (nPossiblyUsedLeaves - nEmptyLeaves);
97119 }
98120
121+ /// @notice Convenience method to return the total weight of the pool
122+ /// @return the total weight of the pool
99123 function totalWeight () public view returns (uint256 ) {
100124 return root.sumWeight ();
101125 }
102126
103- // Give the operator a new ID number
104- // Does not check if the operator already has an ID number
127+ /// @notice Give the operator a new ID number.
128+ /// Does not check if the operator already has an ID number.
129+ /// @param operator the address of the operator
130+ /// @return a new ID for that operator
105131 function allocateOperatorID (address operator ) internal returns (uint256 ) {
106132 uint256 id = idAddress.length ;
107133
@@ -112,36 +138,51 @@ contract SortitionTree {
112138 return id;
113139 }
114140
141+ /// @notice Inserts an operator into the sortition pool
142+ /// @param operator the address of an operator to insert
143+ /// @param weight how much weight that operator has in the pool
115144 function _insertOperator (address operator , uint256 weight ) internal {
116145 require (
117146 ! isOperatorRegistered (operator),
118147 "Operator is already registered in the pool "
119148 );
120149
150+ // Fetch the operator's ID, and if they don't have one, allocate them one.
121151 uint256 id = getOperatorID (operator);
122152 if (id == 0 ) {
123153 id = allocateOperatorID (operator);
124154 }
125155
156+ // Determine which leaf to insert them into
126157 uint256 position = getEmptyLeafPosition ();
127158 // Record the block the operator was inserted in
128159 uint256 theLeaf = Leaf.make (operator, block .number , id);
129160
161+ // Update the leaf, and propagate the weight changes all the way up to the
162+ // root.
130163 root = setLeaf (position, theLeaf, weight, root);
131164
132165 // Without position flags,
133166 // the position 0x000000 would be treated as empty
134167 flaggedLeafPosition[operator] = position.setFlag ();
135168 }
136169
170+ /// @notice Remove an operator (and their weight) from the pool.
171+ /// @param operator the address of the operator to remove
137172 function _removeOperator (address operator ) internal {
138173 uint256 flaggedPosition = getFlaggedLeafPosition (operator);
139174 require (flaggedPosition != 0 , "Operator is not registered in the pool " );
140175 uint256 unflaggedPosition = flaggedPosition.unsetFlag ();
176+
177+ // Update the leaf, and propagate the weight changes all the way up to the
178+ // root.
141179 root = removeLeaf (unflaggedPosition, root);
142180 removeLeafPositionRecord (operator);
143181 }
144182
183+ /// @notice Update an operator's weight in the pool.
184+ /// @param operator the address of the operator to update
185+ /// @param weight the new weight
145186 function updateOperator (address operator , uint256 weight ) internal {
146187 require (
147188 isOperatorRegistered (operator),
@@ -153,19 +194,28 @@ contract SortitionTree {
153194 root = updateLeaf (unflaggedPosition, weight, root);
154195 }
155196
197+ /// @notice Helper method to remove a leaf position record for an operator.
198+ /// @param operator the address of the operator to remove the record for
156199 function removeLeafPositionRecord (address operator ) internal {
157200 flaggedLeafPosition[operator] = 0 ;
158201 }
159202
203+ /// @notice Removes the data and weight from a particular leaf.
204+ /// @param position the leaf index to remove
205+ /// @param _root the root node containing the leaf
206+ /// @return the updated root node
160207 function removeLeaf (uint256 position , uint256 _root )
161208 internal
162209 returns (uint256 )
163210 {
164211 uint256 rightmostSubOne = rightmostLeaf - 1 ;
165212 bool isRightmost = position == rightmostSubOne;
166213
214+ // Clears out the data in the leaf node, and then propagates the weight
215+ // changes all the way up to the root.
167216 uint256 newRoot = setLeaf (position, 0 , 0 , _root);
168217
218+ // Infer if need to fall back on emptyLeaves yet
169219 if (isRightmost) {
170220 rightmostLeaf = rightmostSubOne;
171221 } else {
@@ -174,6 +224,11 @@ contract SortitionTree {
174224 return newRoot;
175225 }
176226
227+ /// @notice Updates the tree to give a particular leaf a new weight.
228+ /// @param position the index of the leaf to update
229+ /// @param weight the new weight
230+ /// @param _root the root node containing the leaf
231+ /// @return the updated root node
177232 function updateLeaf (
178233 uint256 position ,
179234 uint256 weight ,
@@ -186,6 +241,13 @@ contract SortitionTree {
186241 }
187242 }
188243
244+ /// @notice Places a leaf into a particular position, with a given weight and
245+ /// propagates that change.
246+ /// @param position the index to place the leaf in
247+ /// @param theLeaf the new leaf to place in the position
248+ /// @param leafWeight the weight of the leaf
249+ /// @param _root the root containing the new leaf
250+ /// @return the updated root node
189251 function setLeaf (
190252 uint256 position ,
191253 uint256 theLeaf ,
@@ -198,6 +260,12 @@ contract SortitionTree {
198260 return (updateTree (position, leafWeight, _root));
199261 }
200262
263+ /// @notice Propagates a weight change at a position through the tree,
264+ /// eventually returning the updated root.
265+ /// @param position the index of leaf to update
266+ /// @param weight the new weight of the leaf
267+ /// @param _root the root node containing the leaf
268+ /// @return the updated root node
201269 function updateTree (
202270 uint256 position ,
203271 uint256 weight ,
@@ -224,6 +292,10 @@ contract SortitionTree {
224292 return _root.setSlot (childSlot, nodeWeight);
225293 }
226294
295+ /// @notice Retrieves the next available empty leaf position. Tries to fill
296+ /// left to right first, ignoring leaf removals, and then fills
297+ /// most-recent-removals first.
298+ /// @return the position of the empty leaf
227299 function getEmptyLeafPosition () internal returns (uint256 ) {
228300 uint256 rLeaf = rightmostLeaf;
229301 bool spaceOnRight = (rLeaf + 1 ) < Constants.POOL_CAPACITY;
@@ -239,6 +311,9 @@ contract SortitionTree {
239311 }
240312 }
241313
314+ /// @notice Gets the flagged leaf position for an operator.
315+ /// @param operator the address of the operator
316+ /// @return the leaf position of that operator
242317 function getFlaggedLeafPosition (address operator )
243318 internal
244319 view
@@ -247,13 +322,24 @@ contract SortitionTree {
247322 return flaggedLeafPosition[operator];
248323 }
249324
325+ /// @notice Gets the weight of a leaf at a particular position.
326+ /// @param position the index of the leaf
327+ /// @return the weight of the leaf at that position
250328 function getLeafWeight (uint256 position ) internal view returns (uint256 ) {
251329 uint256 slot = position.slot ();
252330 uint256 parent = position.parent ();
331+
332+ // A leaf's weight information is stored a 32-bit slot in the branch layer
333+ // directly above the leaf layer. To access it, we calculate that slot and
334+ // parent position, and always know the hard-coded layer index.
253335 uint256 node = branches[Constants.LEVELS][parent];
254336 return node.getSlot (slot);
255337 }
256338
339+ /// @notice Picks a leaf given a random index.
340+ /// @param index a number in `[0, _root.totalWeight())` used to decide
341+ /// between leaves
342+ /// @param _root the root of the tree
257343 function pickWeightedLeaf (uint256 index , uint256 _root )
258344 internal
259345 view
0 commit comments