forked from decred/dcrd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
init.go
187 lines (163 loc) · 4.02 KB
/
init.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Copyright (c) 2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package chaincfg
import (
"errors"
"fmt"
"strings"
)
var (
ErrDuplicateVoteId = errors.New("duplicate vote id")
ErrInvalidMask = errors.New("invalid mask")
ErrNotConsecutive = errors.New("choices not consecutive")
ErrTooManyChoices = errors.New("too many choices")
ErrInvalidAbstain = errors.New("invalid abstain bits")
ErrInvalidBits = errors.New("invalid vote bits")
ErrInvalidIsAbstain = errors.New("one and only one IsAbstain rule " +
"violation")
ErrInvalidIsNo = errors.New("one and only one IsNo rule violation")
ErrInvalidBothFlags = errors.New("IsNo and IsAbstain may not be both " +
"set to true")
ErrDuplicateChoiceId = errors.New("duplicate choice ID")
)
// bitsSet counts number of bits set.
// Proudly stolen from:
// https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
func bitsSet(bits uint16) uint {
c := uint(0)
for v := bits; v != 0; c++ {
v &= v - 1
}
return c
}
// consecOnes count consecutive 1 bits set.
func consecOnes(bits uint16) uint {
c := uint(0)
for v := bits; v != 0; c++ {
v = v & (v << 1)
}
return c
}
// shift calculates the number of bits that need shifting to get to an index.
func shift(mask uint16) uint {
shift := uint(0)
for {
if mask&0x0001 == 0x0001 {
break
}
shift++
mask >>= 1
}
return shift
}
func validateChoices(mask uint16, choices []Choice) error {
var (
numAbstain, numNo int
)
// Check that mask is consecutive.
if consecOnes(mask) != bitsSet(mask) {
return ErrInvalidMask
}
// Check bits and choice bounds.
if len(choices) > 1<<bitsSet(mask) {
return ErrTooManyChoices
}
dups := make(map[string]struct{})
s := shift(mask)
for index, choice := range choices {
// Check that choice 0 is the abstain vote.
if mask&choice.Bits == 0 && !choice.IsAbstain {
return ErrInvalidAbstain
}
// Check mask bits.
if mask&choice.Bits != choice.Bits {
return ErrInvalidBits
}
// Check that index is consecutive. This test is below the
// Check mask bits one for testing reasons. Leave it here.
if uint16(index) != choice.Bits>>s {
return ErrNotConsecutive
}
// Check that both flags aren't set to true.
if choice.IsAbstain && choice.IsNo {
return ErrInvalidBothFlags
}
// Count flags.
if choice.IsAbstain {
numAbstain++
}
if choice.IsNo {
numNo++
}
// Check for duplicates.
id := strings.ToLower(choice.Id)
_, found := dups[id]
if found {
return ErrDuplicateChoiceId
}
dups[id] = struct{}{}
}
// Check that there is only one IsNo and IsAbstain flag set to true.
if numAbstain != 1 {
return ErrInvalidIsAbstain
}
if numNo != 1 {
return ErrInvalidIsNo
}
return nil
}
func validateAgenda(vote Vote) error {
return validateChoices(vote.Mask, vote.Choices)
}
func validateDeployments(deployments []ConsensusDeployment) (int, error) {
dups := make(map[string]struct{})
for index, deployment := range deployments {
// Check for duplicates.
id := strings.ToLower(deployment.Vote.Id)
_, found := dups[id]
if found {
return index, ErrDuplicateVoteId
}
dups[id] = struct{}{}
}
return -1, nil
}
func validateAgendas() {
for i := 0; i < 3; i++ {
var params Params
switch i {
case 0:
params = MainNetParams
case 1:
params = TestNet2Params
case 2:
params = SimNetParams
default:
panic("invalid net")
}
for version, deployments := range params.Deployments {
index, err := validateDeployments(deployments)
if err != nil {
e := fmt.Sprintf("invalid agenda on %v "+
"version %v id %v: %v", params.Name,
version, deployments[index].Vote.Id,
err)
panic(e)
}
for _, deployment := range deployments {
err := validateAgenda(deployment.Vote)
if err != nil {
e := fmt.Sprintf("invalid agenda "+
"on %v version %v id %v: %v",
params.Name, version,
deployment.Vote.Id, err)
panic(e)
}
}
}
}
}
func init() {
validateAgendas()
}