Newer
Older
"flag"
"fmt"
"log"
"os"
"path"
"sort"
"strconv"
"strings"
latest = -1
minSigners = 5 // We don't want to have less sealers than this number
voteThreshold int = 3 // Number of manual votes needed to enable autovote
var (
url string
json bool
help bool
flags flag.FlagSet
command string
description string
)
func setFlags() {
flags.BoolVar(&json, "json", false, "Produce salida en formato json")
flags.StringVar(&url, "url", "", "URL para conexión con geth. Ejemplo: '/home/bfa/bfa/network/node/geth.ipc' o 'http://localhost:8545'")
flags.BoolVar(&help, "h", false, "")
flags.BoolVar(&help, "help", false, "Muestra esta ayuda")
}
func updateURL(url string) (updated string) {
const (
defaultIPC = "/home/bfa/bfa/network/node/geth.ipc"
defaultHTTP = "http://localhost:8545"
)
if url != "" { // We accept the user selected URL
}
// First, we try IPC
updated = defaultIPC
if bfaNetworkDir := os.Getenv("BFANETWORKDIR"); bfaNetworkDir != "" {
updated = bfaNetworkDir + "/node/get.ipc"
}
if fileInfo, err := os.Stat(updated); err == nil && (fileInfo.Mode()&os.ModeSocket) != 0 {
return
}
updated = defaultHTTP
return
}
func usage(errorCode int) {
_, _ = fmt.Fprintf(os.Stderr, "Uso: %v %v [opciones] %v\n%v\n", os.Args[0], command, otherArgs, description)
flags.PrintDefaults()
os.Exit(errorCode)
}
func parseFlags() {
err := flags.Parse(os.Args[2:])
util.Check(err)
if help {
usage(0)
}
}
func proposals() {
var blockNumber int64
description = "Detalla el estado de las votaciones en curso"
setFlags()
flags.Int64Var(&blockNumber, "block-number", latest, "Número del bloque en el cual se quiere conocer el estado de la propuesta (-1 para el último)")
parseFlags()
url = updateURL(url)
node, err := bfa.Dial(url)
util.Check(err)
defer node.Close()
votes := node.GetVotes(blockNumber)
if json {
util.PrintJson(votes)
return
}
fmt.Printf("Bloque: %d\nPropuestas en curso: %d\n", votes.BlockNumber, len(votes.Proposals))
for _, proposal := range votes.Proposals {
fmt.Printf("Propuesta: %v\n", proposal)
for _, signer := range votes.Signers {
b := votes.Votes[proposal][signer]
var v string
if b == nil {
v = ""
} else {
v = strconv.FormatBool(*b)
}
fmt.Printf("\t%v: %v\n", signer, v)
}
tally := votes.Tally[proposal]
fmt.Printf("A favor: %v, en contra: %v, no votaron: %v\n", tally.True, tally.False, tally.Null)
}
}
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
func parseFormatString(s string) (format string) {
replacements := []struct {
old string
new string
}{
{"YYYY", "2006"},
{"YYY", "006"},
{"YY", "06"},
{"MM", "01"},
{"DD", "02"},
{"hh", "15"},
{"mm", "04"},
{"ss", "05"},
}
switch s {
case "long":
return "2006-01-02 15:04:05"
case "short":
return "15:04:05"
case "unix":
return time.UnixDate
case "rfc3339":
return time.RFC3339
}
format = s
for _, repl := range replacements {
format = strings.Replace(format, repl.old, repl.new, 1)
}
return
}
func sealers() {
var (
blockNumber int64
status bool
length int64 = 10 // timestamp length
)
description = "Presenta la lista de selladores. Opcionalmente indica el último bloque sellado por cada uno."
setFlags()
flags.Int64Var(&blockNumber, "block-number", latest, "Número del bloque en el cual se quiere conocer la lista de selladores (-1 para el último)")
flags.BoolVar(&status, "status", false, "Indica el último bloque sellado por cada sellador, o 0 si un nodo no ha sellado en las últimas 5 rondas.")
flags.BoolVar(×tamp, "timestamp", false, "Muestra el timestamp del sellado en lugar del número de bloque.")
flags.StringVar(&format, "format", "", "Formato del timestamp. Ignorado en formato json. Opciones: 'unix', 'rfc3339', 'long' ('YYYY-MM-DD hh:mm:ss'), 'short' ('hh:mm:ss') o un formato específico. Ejemplo 'DD/MM/YY hh.mm.ss'. También se admite el formato del paquete 'time' de go.")
panic("El bloque génesis no tiene firmantes")
}
url = updateURL(url)
node, err := bfa.Dial(url)
defer node.Close()
if status {
sealers := node.SealersStatus(blockNumber)
if json {
util.PrintJson(sealers)
return
}
var list []string
for sealer := range sealers {
list = append(list, sealer)
}
sort.Slice(list, func(i, j int) bool { return sealers[list[i]].LastBlock > sealers[list[j]].LastBlock })
if !timestamp {
length = util.Max(2, int64(len(strconv.FormatInt(sealers[list[0]].LastBlock, 10))))
} else {
if len(format) > 0 {
format = parseFormatString(format)
length = int64(len(format))
}
var output interface{}
if timestamp {
t := int64(sealers[sealer].Time)
if len(format) > 0 && t > 0 {
output = time.Unix(t, 0).Format(format)
} else {
output = t
}
} else {
output = sealers[sealer].LastBlock
}
fmt.Printf("%v: %*v\n", sealer, length, output)
}
} else {
sealers := node.GetSignersAtBlock(blockNumber)
sort.Slice(sealers, func(i, j int) bool { return sealers[i] < sealers[j] })
if json {
util.PrintJson(sealers)
} else {
for _, sealer := range sealers {
fmt.Println(sealer)
}
}
}
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
func autovote() {
var (
removedSealers = 0
threshold = voteThreshold
voted = make(map[string]bool)
)
description = fmt.Sprintf(`Vota automáticamente en todas las propuestas activas, en el sentido que corresponda, con ciertas restricciones.
- No deja votar si el voto puede reducir la cantidad de selladores a menos de %v.
- No permite eliminar a los selladores establecidos en el bloque génesis.
- Requiere que la propuesta tenga al menos %v votos.
- No permite votar para eliminarse a uno mismo de la lista de selladores.`, minSigners, threshold)
flags.IntVar(&threshold, "threshold", voteThreshold, "Cantidad mínima de votos en una propuesta para habilitar el voto automático.")
setFlags()
parseFlags()
util.PanicIf(threshold < voteThreshold, "No se puede especificar una cantidad de votos inferior a %v.", voteThreshold)
url = updateURL(url)
node, err := bfa.Dial(url)
util.Check(err)
defer node.Close()
util.PanicIf(!node.IsSealer(bfa.Self), "Solo los selladores pueden votar")
votes := node.GetVotes(latest)
genesisSigners := node.GetSignersAtBlock(0)
self, err := node.Coinbase()
util.Check(err)
for _, tally := range votes.Tally {
if tally.False >= threshold { // We are trying to remove a sealer
removedSealers += 1
}
}
util.PanicIf(len(votes.Signers)-removedSealers < minSigners, "No se puede emitir un voto automático que reduzca la cantidad de selladores por debajo de %v.", minSigners)
for _, proposal := range votes.Proposals {
isSealer := util.Contains(votes.Signers, proposal)
switch {
case votes.Votes[proposal][self] != nil:
continue // Already voted
case isSealer && votes.Tally[proposal].False < threshold:
continue // There aren't enough votes to enable autovote
case !isSealer && votes.Tally[proposal].True < threshold:
continue // There aren't enough votes to enable autovote
case util.Contains(genesisSigners, proposal) && isSealer:
log.Printf("No se puede quitar en forma automática a un sellador del bloque génesis: %v.\n", proposal)
continue
case proposal == self:
log.Println("No se puede votar para eliminarse uno mismo de la lista de selladores.")
continue
}
node.Propose(proposal, !isSealer)
if json {
voted[proposal] = !isSealer
} else {
fmt.Printf("Voto por %v: %v\n", proposal, !isSealer)
}
}
if json {
util.PrintJson(voted)
}
}
)
description = "Vota por una propuesta."
flags.BoolVar(&authorize, "authorize", true, "Sentido del voto (true: a favor, false: en contra).")
parseFlags()
url = updateURL(url)
node, err := bfa.Dial(url)
util.Check(err)
defer node.Close()
util.PanicIf(!node.IsSealer(bfa.Self), "Solo los selladores pueden votar")
votes := node.GetVotes(latest)
if flags.NArg() == 0 {
panic("No se especificaron candidatos por los cuales votar")
}
for i := 0; i < flags.NArg(); i++ {
address := flags.Arg(i)
if !util.IsAddress(address) {
panic(fmt.Sprintf("'%v' no es una dirección válida", address))
if _, ok := votes.Tally[address]; ok {
continue // address is in a proposal, so we allow voting either way
isSealer := util.Contains(votes.Signers, address)
switch { // address is not in a proposal, we allow removing signers or adding non signers
case isSealer && authorize:
panic(fmt.Sprintf("'%v' ya es un sellador", address))
case !isSealer && !authorize:
panic(fmt.Sprintf("'%v' no es un sellador", address))
node.Propose(address, authorize)
if json {
voted[address] = authorize
} else {
fmt.Printf("Voto por %v: %v\n", address, authorize)
if json {
util.PrintJson(voted)
commands = map[string]func(){
"proposals": proposals,
"sealers": sealers,
for command := range commands {
validCommands = append(validCommands, command)
}
defer func() {
if err := recover(); err != nil {
}
}()
if len(os.Args) > 1 {
command = os.Args[1]
}
log.Printf("Uso: %v <%v> [opciones]\n", path.Base(os.Args[0]), strings.Join(validCommands, "|"))
log.Printf("Para ayuda: %v <command> -h\n", path.Base(os.Args[0]))