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