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  }