@@ -17,20 +17,13 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
1717    // *         Enums / Structs           * // 
1818    // ************************************* // 
1919
20-     struct SubCourtStakes   {
21-         uint256  totalStakedInSubCourts;
22-         uint96 [MAX_STAKE_PATHS] subCourtIDs;
23-         uint256 [MAX_STAKE_PATHS] stakedInSubCourts;
24-     }
25- 
2620    struct SortitionSumTree   {
2721        uint256  K; // The maximum number of children per node. 
2822        uint256 [] stack; // We use this to keep track of vacant positions in the tree after removing a leaf. This is for keeping the tree as balanced as possible without spending gas on moving nodes around. 
2923        uint256 [] nodes; // The tree nodes. 
3024        // Two-way mapping of IDs to node indexes. Note that node index 0 is reserved for the root node, and means the ID does not have a node. 
3125        mapping (bytes32  stakePathID = >  uint256  nodeIndex ) IDsToNodeIndexes;
3226        mapping (uint256  nodeIndex = >  bytes32  stakePathID ) nodeIndexesToIDs;
33-         mapping (bytes32  stakePathID = >  SubCourtStakes subcourtStakes ) IDsToSubCourtStakes;
3427    }
3528
3629    struct DelayedStake   {
@@ -322,27 +315,30 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
322315    /// @param _courtID The ID of the court. 
323316    /// @param _penalty The amount of PNK to be deducted. 
324317    /// @return pnkBalance The updated total PNK balance of the juror, including the penalty. 
318+     /// @return newCourtStake The updated stake of the juror in the court. 
325319    /// @return availablePenalty The amount of PNK that was actually deducted. 
326320    function setStakePenalty  (
327321        address  _account ,
328322        uint96  _courtID ,
329323        uint256  _penalty 
330-     ) external  override  onlyByCore returns  (uint256  pnkBalance , uint256  availablePenalty ) {
324+     ) external  override  onlyByCore returns  (uint256  pnkBalance , uint256  newCourtStake ,  uint256   availablePenalty ) {
331325        Juror storage  juror =  jurors[_account];
332326        availablePenalty =  _penalty;
327+         newCourtStake =  stakeOf (_account, _courtID);
333328        if  (juror.stakedPnk <  _penalty) {
334329            availablePenalty =  juror.stakedPnk;
335330        }
336331
337-         if  (availablePenalty ==  0 ) return  (juror.stakedPnk, 0 ); // No penalty to apply. 
332+         if  (availablePenalty ==  0 ) return  (juror.stakedPnk, newCourtStake,  0 ); // No penalty to apply. 
338333
339334        uint256  currentStake =  stakeOf (_account, _courtID);
340335        uint256  newStake =  0 ;
341336        if  (currentStake >=  availablePenalty) {
342337            newStake =  currentStake -  availablePenalty;
343338        }
344339        _setStake (_account, _courtID, 0 , availablePenalty, newStake);
345-         pnkBalance =  juror.stakedPnk; // Updated by _setStake(). 
340+         pnkBalance =  juror.stakedPnk; // updated by _setStake() 
341+         newCourtStake =  stakeOf (_account, _courtID); // updated by _setStake() 
346342    }
347343
348344    function _setStake  (
@@ -378,14 +374,12 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
378374        bytes32  stakePathID =  _accountAndCourtIDToStakePathID (_account, _courtID);
379375        bool  finished =  false ;
380376        uint96  currentCourtID =  _courtID;
381-         uint96  fromSubCourtID =  0 ; // 0 means it is not coming from a subcourt. 
382377        while  (! finished) {
383378            // Tokens are also implicitly staked in parent courts through sortition module to increase the chance of being drawn. 
384-             _set (bytes32 (uint256 (currentCourtID)), _newStake, stakePathID, fromSubCourtID );
379+             _set (bytes32 (uint256 (currentCourtID)), _newStake, stakePathID);
385380            if  (currentCourtID ==  GENERAL_COURT) {
386381                finished =  true ;
387382            } else  {
388-                 fromSubCourtID =  currentCourtID;
389383                (currentCourtID, , , , , , ) =  core.courts (currentCourtID); // Get the parent court. 
390384            }
391385        }
@@ -486,31 +480,7 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
486480        }
487481
488482        bytes32  stakePathID =  tree.nodeIndexesToIDs[treeIndex];
489-         drawnAddress =  _stakePathIDToAccount (stakePathID);
490- 
491-         // Identify which subcourt was selected based on currentDrawnNumber 
492-         SubCourtStakes storage  subcourtStakes =  tree.IDsToSubCourtStakes[stakePathID];
493- 
494-         // The current court stake is the node value minus all subcourt stakes 
495-         uint256  currentCourtStake =  0 ;
496-         if  (tree.nodes[treeIndex] >  subcourtStakes.totalStakedInSubCourts) {
497-             currentCourtStake =  tree.nodes[treeIndex] -  subcourtStakes.totalStakedInSubCourts;
498-         }
499- 
500-         // Check if the drawn number falls within current court range 
501-         if  (currentDrawnNumber >=  currentCourtStake) {
502-             // Find which subcourt range contains the drawn number 
503-             uint256  accumulatedStake =  currentCourtStake;
504-             for  (uint256  i =  0 ; i <  MAX_STAKE_PATHS; i++ ) {
505-                 if  (subcourtStakes.stakedInSubCourts[i] >  0 ) {
506-                     if  (currentDrawnNumber <  accumulatedStake +  subcourtStakes.stakedInSubCourts[i]) {
507-                         fromSubcourtID =  subcourtStakes.subCourtIDs[i];
508-                         break ;
509-                     }
510-                     accumulatedStake +=  subcourtStakes.stakedInSubCourts[i];
511-                 }
512-             }
513-         }
483+         (drawnAddress, fromSubcourtID) =  _stakePathIDToAccountAndCourtID (stakePathID);
514484    }
515485
516486    /// @dev Get the stake of a juror in a court. 
@@ -524,11 +494,11 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
524494
525495    /// @dev Get the stake of a juror in a court. 
526496    /// @param _key The key of the tree, corresponding to a court. 
527-     /// @param _ID  The stake path ID, corresponding to a juror. 
497+     /// @param _stakePathID  The stake path ID, corresponding to a juror. 
528498    /// @return The stake of the juror in the court. 
529-     function stakeOf  (bytes32  _key , bytes32  _ID ) public  view  returns  (uint256 ) {
499+     function stakeOf  (bytes32  _key , bytes32  _stakePathID ) public  view  returns  (uint256 ) {
530500        SortitionSumTree storage  tree =  sortitionSumTrees[_key];
531-         uint  treeIndex =  tree.IDsToNodeIndexes[_ID ];
501+         uint  treeIndex =  tree.IDsToNodeIndexes[_stakePathID ];
532502        if  (treeIndex ==  0 ) {
533503            return  0 ;
534504        }
@@ -601,59 +571,6 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
601571        }
602572    }
603573
604-     /// @dev Update the subcourt stakes. 
605-     /// @param _subcourtStakes The subcourt stakes. 
606-     /// @param _value The value to update. 
607-     /// @param _fromSubCourtID The ID of the subcourt from which the stake is coming from, or 0 if the stake does not come from a subcourt. 
608-     /// `O(1)` complexity with `MAX_STAKE_PATHS` a small constant. 
609-     function _updateSubCourtStakes  (
610-         SubCourtStakes storage  _subcourtStakes ,
611-         uint256  _value ,
612-         uint96  _fromSubCourtID 
613-     ) internal  {
614-         // Update existing stake item if found 
615-         for  (uint256  i =  0 ; i <  MAX_STAKE_PATHS; i++ ) {
616-             if  (_subcourtStakes.subCourtIDs[i] ==  _fromSubCourtID) {
617-                 if  (_value ==  0 ) {
618-                     delete  _subcourtStakes.subCourtIDs[i];
619-                     delete  _subcourtStakes.stakedInSubCourts[i];
620-                 } else  {
621-                     _subcourtStakes.totalStakedInSubCourts +=  _value;
622-                     _subcourtStakes.totalStakedInSubCourts -=  _subcourtStakes.stakedInSubCourts[i];
623-                     _subcourtStakes.stakedInSubCourts[i] =  _value;
624-                 }
625-                 return ;
626-             }
627-         }
628-         // Not found so add a new stake item 
629-         for  (uint256  i =  0 ; i <  MAX_STAKE_PATHS; i++ ) {
630-             if  (_subcourtStakes.subCourtIDs[i] ==  0 ) {
631-                 _subcourtStakes.subCourtIDs[i] =  _fromSubCourtID;
632-                 _subcourtStakes.totalStakedInSubCourts +=  _value;
633-                 _subcourtStakes.stakedInSubCourts[i] =  _value;
634-                 return ;
635-             }
636-         }
637-     }
638- 
639-     /// @dev Retrieves a juror's address from the stake path ID. 
640-     /// @param _stakePathID The stake path ID to unpack. 
641-     /// @return account The account. 
642-     function _stakePathIDToAccount  (bytes32  _stakePathID ) internal  pure  returns  (address  account ) {
643-         assembly  {
644-             // solium-disable-line security/no-inline-assembly 
645-             let  ptr :=  mload (0x40 )
646-             for  {
647-                 let  i :=  0x00 
648-             } lt (i, 0x14 ) {
649-                 i :=  add (i, 0x01 )
650-             } {
651-                 mstore8 (add (add (ptr, 0x0c ), i), byte (i, _stakePathID))
652-             }
653-             account :=  mload (ptr)
654-         }
655-     }
656- 
657574    function _extraDataToTreeK  (bytes  memory  _extraData ) internal  pure  returns  (uint256  K ) {
658575        if  (_extraData.length  >=  32 ) {
659576            assembly  {
@@ -668,14 +585,13 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
668585    /// @dev Set a value in a tree. 
669586    /// @param _key The key of the tree. 
670587    /// @param _value The new value. 
671-     /// @param _ID The ID of the value. 
672-     /// @param _fromSubCourtID The ID of the subcourt from which the stake is coming from, or 0 if the stake does not come from a subcourt. 
588+     /// @param _stakePathID The ID of the value. 
673589    /// `O(log_k(n))` where 
674590    /// `k` is the maximum number of children per node in the tree, 
675591    ///  and `n` is the maximum number of nodes ever appended. 
676-     function _set  (bytes32  _key , uint256  _value , bytes32  _ID ,  uint96   _fromSubCourtID ) internal  {
592+     function _set  (bytes32  _key , uint256  _value , bytes32  _stakePathID ) internal  {
677593        SortitionSumTree storage  tree =  sortitionSumTrees[_key];
678-         uint256  treeIndex =  tree.IDsToNodeIndexes[_ID ];
594+         uint256  treeIndex =  tree.IDsToNodeIndexes[_stakePathID ];
679595
680596        if  (treeIndex ==  0 ) {
681597            // No existing node. 
@@ -709,8 +625,8 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
709625                }
710626
711627                // Add label. 
712-                 tree.IDsToNodeIndexes[_ID ] =  treeIndex;
713-                 tree.nodeIndexesToIDs[treeIndex] =  _ID ;
628+                 tree.IDsToNodeIndexes[_stakePathID ] =  treeIndex;
629+                 tree.nodeIndexesToIDs[treeIndex] =  _stakePathID ;
714630
715631                _updateParents (_key, treeIndex, true , _value);
716632            }
@@ -727,7 +643,7 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
727643                tree.stack.push (treeIndex);
728644
729645                // Clear label. 
730-                 delete  tree.IDsToNodeIndexes[_ID ];
646+                 delete  tree.IDsToNodeIndexes[_stakePathID ];
731647                delete  tree.nodeIndexesToIDs[treeIndex];
732648
733649                _updateParents (_key, treeIndex, false , value);
@@ -743,11 +659,9 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
743659                _updateParents (_key, treeIndex, plusOrMinus, plusOrMinusValue);
744660            }
745661        }
746- 
747-         _updateSubCourtStakes (tree.IDsToSubCourtStakes[_ID], _value, _fromSubCourtID);
748662    }
749663
750-     /// @dev Packs an account and a court ID into a stake path ID. 
664+     /// @dev Packs an account and a court ID into a stake path ID: [20 bytes of address][12 bytes of courtID] = 32 bytes total . 
751665    /// @param _account The address of the juror to pack. 
752666    /// @param _courtID The court ID to pack. 
753667    /// @return stakePathID The stake path ID. 
@@ -758,13 +672,17 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
758672        assembly  {
759673            // solium-disable-line security/no-inline-assembly 
760674            let  ptr :=  mload (0x40 )
675+ 
676+             // Write account address (first 20 bytes) 
761677            for  {
762678                let  i :=  0x00 
763679            } lt (i, 0x14 ) {
764680                i :=  add (i, 0x01 )
765681            } {
766682                mstore8 (add (ptr, i), byte (add (0x0c , i), _account))
767683            }
684+ 
685+             // Write court ID (last 12 bytes) 
768686            for  {
769687                let  i :=  0x14 
770688            } lt (i, 0x20 ) {
@@ -776,6 +694,39 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
776694        }
777695    }
778696
697+     /// @dev Retrieves both juror's address and court ID from the stake path ID. 
698+     /// @param _stakePathID The stake path ID to unpack. 
699+     /// @return account The account. 
700+     /// @return courtID The court ID. 
701+     function _stakePathIDToAccountAndCourtID  (
702+         bytes32  _stakePathID 
703+     ) internal  pure  returns  (address  account , uint96  courtID ) {
704+         assembly  {
705+             // solium-disable-line security/no-inline-assembly 
706+             let  ptr :=  mload (0x40 )
707+ 
708+             // Read account address (first 20 bytes) 
709+             for  {
710+                 let  i :=  0x00 
711+             } lt (i, 0x14 ) {
712+                 i :=  add (i, 0x01 )
713+             } {
714+                 mstore8 (add (add (ptr, 0x0c ), i), byte (i, _stakePathID))
715+             }
716+             account :=  mload (ptr)
717+ 
718+             // Read court ID (last 12 bytes) 
719+             for  {
720+                 let  i :=  0x00 
721+             } lt (i, 0x0c ) {
722+                 i :=  add (i, 0x01 )
723+             } {
724+                 mstore8 (add (add (ptr, 0x14 ), i), byte (add (i, 0x14 ), _stakePathID))
725+             }
726+             courtID :=  mload (ptr)
727+         }
728+     }
729+ 
779730    // ************************************* // 
780731    // *              Errors               * // 
781732    // ************************************* // 
0 commit comments