diff --git a/cmd/bfa_client/bfa_client.go b/cmd/bfa_client/bfa_client.go index 2025016589e0d0e1ba878b3b6c24ccbb7bdad42a..4ba1ab506cb83fce825fe534cf1920980449ffb1 100644 --- a/cmd/bfa_client/bfa_client.go +++ b/cmd/bfa_client/bfa_client.go @@ -1,8 +1,10 @@ package main import ( + "encoding/json" "flag" "fmt" + "io/ioutil" "log" "math/big" "os" @@ -24,7 +26,7 @@ const ( var ( url string - json bool + jsonOutput bool help bool flags = flag.NewFlagSet("", flag.ExitOnError) description string @@ -57,7 +59,7 @@ var ( ) func setFlags() { - flags.BoolVar(&json, "json", false, "Produce salida en formato json") + flags.BoolVar(&jsonOutput, "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") @@ -114,7 +116,7 @@ func proposals() { defer node.Close() blockNumber = node.BlockNumberInRange(blockNumber) votes := node.Votes(blockNumber) - if json { + if jsonOutput { util.PrintJson(votes) return } @@ -169,7 +171,7 @@ func parseFormatString(s string) (format string) { func printSealers(sealers []bfa.Address) { bfa.SortAddresses(sealers) - if json { + if jsonOutput { util.PrintJson(sealers) } else { for _, sealer := range sealers { @@ -236,7 +238,7 @@ func sealers() { } sealers := node.SealersStatus(blockNumber) - if json { + if jsonOutput { util.PrintJson(sealers) return } @@ -359,13 +361,13 @@ func autovote() { continue } node.Propose(proposal, !isSealer) - if json { + if jsonOutput { voted[proposal.String()] = !isSealer } else { fmt.Printf("Voto por %v: %v\n", proposal, !isSealer) } } - if json { + if jsonOutput { util.PrintJson(voted) } } @@ -401,13 +403,13 @@ func propose() { } } node.Propose(address, authorize) - if json { + if jsonOutput { voted[address.String()] = authorize } else { fmt.Printf("Voto por %v: %v\n", address, authorize) } } - if json { + if jsonOutput { util.PrintJson(voted) } } @@ -546,7 +548,7 @@ func transfers() { src := transaction.From dst := transaction.To if len(set) == 0 || set[src] || set[dst] { - if json { + if jsonOutput { txs = append(txs, Transfer{src, dst, transaction.Value, i}) } else { fmt.Printf("%v -> %v: %v (%v)\n", src.Hex(), dst.Hex(), transaction.Value, transaction.BlockNumber) @@ -566,32 +568,27 @@ func transfers() { } } } - if json { + if jsonOutput { util.PrintJson(txs) } } -func sealerstats() { - type Stats struct { - FirstSeen int64 `json:"first_seen,omitempty"` - LastSeen int64 `json:"last_seen,omitempty"` - FirstBlockSealed int64 `json:"first_block_sealed,omitempty"` - LastBlockSealed int64 `json:"last_block_sealed,omitempty"` - BlocksSealed int64 `json:"blocks_sealed"` - BlocksAlive int64 `json:"blocks_alive"` - Availability float64 `json:"availability"` - recent bool - } +// Stats collect statistics about a sealer +type Stats struct { + FirstSeen int64 `json:"first_seen,omitempty"` + LastSeen int64 `json:"last_seen,omitempty"` + FirstBlockSealed int64 `json:"first_block_sealed,omitempty"` + LastBlockSealed int64 `json:"last_block_sealed,omitempty"` + BlocksSealed int64 `json:"blocks_sealed"` + BlocksAlive int64 `json:"blocks_alive"` + Availability float64 `json:"availability"` + recent bool +} + +func calculateSealerStats(url string, first int64, last int64, factor int64) (sealerStats map[bfa.Address]*Stats) { var ( - first, last, end, lastBlock int64 - sealerStats map[bfa.Address]*Stats + lastBlock, end int64 ) - description = "Calcula estadÃsticas de los selladores." - setFlags() - flags.Int64Var(&first, "first-block", 1, "Primer bloque del rango (Número negativo para restar del último bloque)") - flags.Int64Var(&last, "last-block", latest, "Último bloque del rango (-1 para especificar el último bloque)") - parseFlags(true) - url = updateURL(url) node, err := bfa.Dial(url) util.Check(err) defer node.Close() @@ -618,7 +615,7 @@ func sealerstats() { for block := first; block < end; { snapshot := node.SnapshotAtBlock(block) recentsLength := int64(len(snapshot.Recents)) - shouldHaveSealedAfter := block - 4*recentsLength + shouldHaveSealedAfter := block - factor*int64(len(snapshot.Signers)) if shouldHaveSealedAfter < 0 { shouldHaveSealedAfter = 0 } @@ -645,42 +642,44 @@ func sealerstats() { } } for sealer := range snapshot.Signers { - stats := sealerStats[sealer] - stats.LastSeen = block - var firstSeen bool - if stats.FirstSeen == 0 { - // we have never seen this sealer before - fmt.Fprintf(os.Stderr, "Nuevo sellador: %v\n", sealer.Hex()) - stats.FirstSeen = block - firstSeen = true - } - if !stats.recent { - // sealer is not in snapshot.Recents - if stats.FirstBlockSealed > 0 { - // Sealer has signed at least once, we never zero its BlocksAlive - if stats.LastBlockSealed > shouldHaveSealedAfter { + if stats, ok := sealerStats[sealer]; ok { + // We are only interested if the sealer is in sealersStats + stats.LastSeen = block + var firstSeen bool + if stats.FirstSeen == 0 { + // we have never seen this sealer before + fmt.Fprintf(os.Stderr, "Nuevo sellador: %v\n", sealer.Hex()) + stats.FirstSeen = block + firstSeen = true + } + if !stats.recent { + // sealer is not in snapshot.Recents + if stats.FirstBlockSealed > 0 { + // Sealer has signed at least once, we never zero its BlocksAlive + if stats.LastBlockSealed > shouldHaveSealedAfter { + stats.BlocksAlive += blocksProcessed + continue + } + } else if stats.FirstSeen <= shouldHaveSealedAfter { + // Sealer has never signed since we've seen it for the first time + stats.BlocksAlive = 0 + } else if !firstSeen { + // Sealer has never signed, but only a few blocks have passed since we've seen it for the first time + // and we've seen it more than once stats.BlocksAlive += blocksProcessed - continue + } else { + stats.BlocksAlive = 1 } - } else if stats.FirstSeen <= shouldHaveSealedAfter { - // Sealer has never signed since we've seen it for the first time - stats.BlocksAlive = 0 - } else if !firstSeen { - // Sealer has never signed, but only a few blocks have passed since we've seen it for the first time - // and we've seen it more than once - stats.BlocksAlive += blocksProcessed + continue + } + stats.recent = false + // sealer is in snapshot.Recents + if firstSeen { + stats.FirstSeen = stats.FirstBlockSealed + stats.BlocksAlive = block - stats.FirstSeen + 1 } else { - stats.BlocksAlive = 1 + stats.BlocksAlive += blocksProcessed } - continue - } - stats.recent = false - // sealer is in snapshot.Recents - if firstSeen { - stats.FirstSeen = stats.FirstBlockSealed - stats.BlocksAlive = block - stats.FirstSeen + 1 - } else { - stats.BlocksAlive += blocksProcessed } } lastBlock = block @@ -703,6 +702,40 @@ func sealerstats() { blocksSeen := stats.LastSeen - stats.FirstSeen + 1 stats.Availability = float64(stats.BlocksAlive) / float64(blocksSeen) } + return +} + +func readStatsFromFile(filename string) (sealerStats map[bfa.Address]*Stats) { + file, err := os.Open(filename) + util.Check(err) + defer file.Close() + byteBuffer, err := ioutil.ReadAll(file) + util.Check(err) + err = json.Unmarshal(byteBuffer, &sealerStats) + util.Check(err) + + return +} + +func sealerstats() { + var ( + first, last, factor int64 + jsonFile string + sealerStats map[bfa.Address]*Stats + ) + description = "Calcula estadÃsticas de los selladores." + setFlags() + flags.Int64Var(&first, "first-block", 1, "Primer bloque del rango (Número negativo para restar del último bloque). Ignorado si se especificó 'json_file'") + flags.Int64Var(&last, "last-block", latest, "Último bloque del rango (-1 para especificar el último bloque). Ignorado si se especificó 'json_file'") + flags.Int64Var(&factor, "factor", 2, "Bloques necesarios para declarar un nodo como no activo, especificados como múltiplos de la cantidad de selladores") + flags.StringVar(&jsonFile, "json-file", "", "Lee los datos de un archivo en lugar de recorrer la cadena") + parseFlags(true) + url = updateURL(url) + if len(jsonFile) > 0 { + sealerStats = readStatsFromFile(jsonFile) + } else { + sealerStats = calculateSealerStats(url, first, last, factor) + } util.PrintJson(sealerStats) }