github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/acm/validator/bucket.go (about) 1 package validator 2 3 import ( 4 "fmt" 5 "math/big" 6 7 "github.com/hyperledger/burrow/crypto" 8 "github.com/tendermint/tendermint/types" 9 ) 10 11 // Safety margin determined by Tendermint (see comment on source constant) 12 var maxTotalPower = big.NewInt(types.MaxTotalVotingPower) 13 var minTotalPower = big.NewInt(4) 14 15 type Bucket struct { 16 // Delta tracks the changes to validator power made since the previous rotation 17 Delta *Set 18 // Previous the value for all validator powers at the point of the last rotation 19 // (the sum of all the deltas over all rotations) - these are the history of the complete validator sets at each rotation 20 Previous *Set 21 // Tracks the current working version of the next set; Previous + Delta 22 Next *Set 23 // Flow tracks the absolute value of all flows (difference between previous cum bucket and current delta) towards and away from each validator (tracking each validator separately to avoid double counting flows made against the same validator 24 Flow *Set 25 } 26 27 func NewBucket(initialSets ...Iterable) *Bucket { 28 bucket := &Bucket{ 29 Previous: NewTrimSet(), 30 Next: NewTrimSet(), 31 Delta: NewSet(), 32 Flow: NewSet(), 33 } 34 for _, vs := range initialSets { 35 vs.IterateValidators(func(id crypto.Addressable, power *big.Int) error { 36 bucket.Previous.ChangePower(id.GetPublicKey(), power) 37 bucket.Next.ChangePower(id.GetPublicKey(), power) 38 return nil 39 }) 40 } 41 return bucket 42 } 43 44 // Implement Reader 45 func (vc *Bucket) Power(id crypto.Address) (*big.Int, error) { 46 return vc.Previous.Power(id) 47 } 48 49 // SetPower ensures that validator power would not change too quickly in a single block 50 func (vc *Bucket) SetPower(id *crypto.PublicKey, power *big.Int) (*big.Int, error) { 51 const errHeader = "Bucket.SetPower():" 52 err := checkPower(power) 53 if err != nil { 54 return nil, fmt.Errorf("%s %v", errHeader, err) 55 } 56 57 nextTotalPower := vc.Next.TotalPower() 58 nextTotalPower.Add(nextTotalPower, vc.Next.Flow(id, power)) 59 // We must not have lower validator power than 4 because this would prevent any flow from occurring 60 // min > nextTotalPower 61 if minTotalPower.Cmp(nextTotalPower) == 1 { 62 return nil, fmt.Errorf("%s cannot change validator power of %v from %v to %v because that would result "+ 63 "in a total power less than the permitted minimum of 4: would make next total power: %v", 64 errHeader, id.GetAddress(), vc.Previous.GetPower(id.GetAddress()), power, nextTotalPower) 65 } 66 67 // nextTotalPower > max 68 if nextTotalPower.Cmp(maxTotalPower) == 1 { 69 return nil, fmt.Errorf("%s cannot change validator power of %v from %v to %v because that would result "+ 70 "in a total power greater than that allowed by tendermint (%v): would make next total power: %v", 71 errHeader, id.GetAddress(), vc.Previous.GetPower(id.GetAddress()), power, maxTotalPower, nextTotalPower) 72 } 73 74 // The new absolute flow caused by this AlterPower 75 flow := vc.Previous.Flow(id, power) 76 absFlow := new(big.Int).Abs(flow) 77 78 // Only check flow if power exists, this allows us to 79 // bootstrap the set from an empty state 80 if vc.Previous.TotalPower().Sign() > 0 { 81 // The max flow we are permitted to allow across all validators 82 maxFlow := vc.Previous.MaxFlow() 83 // The remaining flow we have to play with 84 allowableFlow := new(big.Int).Sub(maxFlow, vc.Flow.totalPower) 85 86 // If we call vc.flow.ChangePower(id, absFlow) (below) will we induce a change in flow greater than the allowable 87 // flow we have left to spend? 88 if vc.Flow.Flow(id, absFlow).Cmp(allowableFlow) == 1 { 89 return nil, fmt.Errorf("%s cannot change validator power of %v from %v to %v because that would result "+ 90 "in a flow greater than or equal to 1/3 of total power for the next commit: flow induced by change: %v, "+ 91 "current total flow: %v/%v (cumulative/max), remaining allowable flow: %v", 92 errHeader, id.GetAddress(), vc.Previous.GetPower(id.GetAddress()), power, absFlow, vc.Flow.totalPower, 93 maxFlow, allowableFlow) 94 } 95 } 96 // Set flow for this id to update flow.totalPower (total flow) for comparison below, keep track of flow for each id 97 // so that we only count flow once for each id 98 vc.Flow.ChangePower(id, absFlow) 99 // Update Delta and Next 100 vc.Delta.ChangePower(id, power) 101 vc.Next.ChangePower(id, power) 102 return absFlow, nil 103 } 104 105 func (vc *Bucket) CurrentSet() *Set { 106 return vc.Previous 107 } 108 109 func (vc *Bucket) String() string { 110 return fmt.Sprintf("Bucket{Previous: %v; Next: %v; Delta: %v}", vc.Previous, vc.Next, vc.Delta) 111 } 112 113 func (vc *Bucket) Equal(vwOther *Bucket) error { 114 err := vc.Delta.Equal(vwOther.Delta) 115 if err != nil { 116 return fmt.Errorf("bucket delta != other bucket delta: %v", err) 117 } 118 err = vc.Previous.Equal(vwOther.Previous) 119 if err != nil { 120 return fmt.Errorf("bucket cum != other bucket cum: %v", err) 121 } 122 return nil 123 } 124 125 func checkPower(power *big.Int) error { 126 if power.Sign() == -1 { 127 return fmt.Errorf("cannot set negative validator power: %v", power) 128 } 129 if !power.IsInt64() { 130 return fmt.Errorf("for tendermint compatibility validator power must fit within an int but %v "+ 131 "does not", power) 132 } 133 return nil 134 }