Skip to content
Snippets Groups Projects
Commit b8f583d3 authored by Miguel Montes's avatar Miguel Montes
Browse files

Versión modificada de bfa_client

parents
No related branches found
No related tags found
No related merge requests found
bin/
package main
import (
"flag"
"fmt"
"log"
"math/big"
"os"
"path"
"sort"
"strconv"
"strings"
"time"
"gitlab.bfa.ar/miguel/bfa/internal/bfa"
"gitlab.bfa.ar/miguel/bfa/internal/util"
)
const (
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.NewFlagSet("", flag.ExitOnError)
description string
otherArgs string
wei = new(big.Float).SetFloat64(1)
kilowei = new(big.Float).SetFloat64(1e3)
megawei = new(big.Float).SetFloat64(1e6)
gigawei = new(big.Float).SetFloat64(1e9)
microether = new(big.Float).SetFloat64(1e12)
milliether = new(big.Float).SetFloat64(1e15)
ether = new(big.Float).SetFloat64(1e18)
units = map[string]*big.Float{
"wei": wei,
"kwei": kilowei,
"kilowei": kilowei,
"babbage": kilowei,
"mwei": megawei,
"megawei": megawei,
"lovelace": megawei,
"gwei": gigawei,
"gigawei": gigawei,
"shannon": gigawei,
"microether": microether,
"szabo": microether,
"milliether": milliether,
"finney": milliether,
"ether": ether,
}
)
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
return 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) {
util.Require(len(os.Args) > 1, "not enough arguments")
_, _ = fmt.Fprintf(os.Stderr, "Uso: %v %v [opciones] %v\n%v\n", os.Args[0], os.Args[1], otherArgs, description)
flags.PrintDefaults()
os.Exit(errorCode)
}
func parseFlags(allowAdditionalArgs bool) {
err := flags.Parse(os.Args[2:])
util.Check(err)
if help {
usage(0)
}
if flags.NArg() > 0 && !allowAdditionalArgs {
_, _ = fmt.Fprintln(os.Stderr, "Demasiados argumentos")
usage(1)
}
}
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(false)
url = updateURL(url)
node, err := bfa.Dial(url)
util.Check(err)
defer node.Close()
blockNumber = node.BlockNumberInRange(blockNumber)
votes := node.Votes(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.Hex())
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.Hex(), v)
}
tally := votes.Tally[proposal]
fmt.Printf("A favor: %v, en contra: %v, no votaron: %v\n", tally.True, tally.False, tally.Null)
}
}
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 printSealers(sealers []bfa.Address) {
bfa.SortAddresses(sealers)
if json {
util.PrintJson(sealers)
} else {
for _, sealer := range sealers {
fmt.Println(sealer.Hex())
}
}
}
func weiTo(wei *big.Int, unit string) (value *big.Float, ok bool) {
divisor, ok := units[unit]
if ok {
w := new(big.Float).SetInt(wei)
value = w.Quo(w, divisor)
}
return
}
func sealers() {
var (
blockNumber int64
lastBlock bool
timestamp bool
difficulty bool
balance bool
header bool
format string
formatStr string
headerFmt string
unit string
list []bfa.Address
lastBlockLen int64
timestampLen int64
balanceLen int64
)
description = "Presenta la lista de selladores. Opcionalmente presenta información sobre los selladores."
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(&lastBlock, "last-block", false, "Muestra el último bloque sellado por cada sellador, o 0 si un nodo no ha sellado en las últimas 5 rondas.")
flags.BoolVar(&timestamp, "timestamp", false, "Muestra el timestamp del sellado.")
flags.BoolVar(&difficulty, "difficulty", false, "Muestra la dificultad del sellado (1: fuera de turno, 2: en turno).")
flags.BoolVar(&balance, "balance", false, "Muestra el saldo del sellador (en Wei).")
flags.BoolVar(&header, "header", false, "Muestra un encabezado en cada columna.")
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.")
flags.StringVar(&formatStr, "output-format", "", "Formato de la salida. Ignorado en formato json. Es una string en la que se reemplazan las ocurrencias de {sealer}, {last-block}, {timestamp}, {difficulty} y {balance} por sus respectivos valores.")
flags.StringVar(&unit, "unit", "wei", "Unidades en la que se expresa el balance. Posibles valores: wei, Kwei, kilowei, Mwei, megawei, Gwei, gigawei, microether, milliether, ether, babbage, lovelace, shannon, szabo, finney.")
parseFlags(false)
if blockNumber == 0 {
util.Error("El bloque génesis no tiene firmantes\n")
}
unit = strings.ToLower(unit)
if _, ok := units[unit]; !ok {
util.Error("Unidad '%v' desconocida\n", unit)
}
url = updateURL(url)
node, err := bfa.Dial(url)
util.Check(err)
defer node.Close()
blockNumber = node.BlockNumberInRange(blockNumber)
extended := lastBlock || timestamp || difficulty || balance || len(formatStr) > 0
if !extended {
printSealers(node.SealersAtBlock(blockNumber))
return
}
sealers := node.SealersStatus(blockNumber)
if json {
util.PrintJson(sealers)
return
}
if len(format) > 0 {
format = parseFormatString(format)
timestampLen = int64(len(format))
} else {
timestampLen = 10
}
for sealer := range sealers {
list = append(list, sealer)
}
if formatStr != "" {
bfa.SortAddresses(list)
formatStr = strings.Replace(formatStr, "%", "%%", -1)
formatStr = strings.Replace(formatStr, "{sealer}", "%[1]v", -1)
formatStr = strings.Replace(formatStr, "{last-block}", "%[3]v", -1)
formatStr = strings.Replace(formatStr, "{timestamp}", "%[5]v", -1)
formatStr = strings.Replace(formatStr, "{difficulty}", "%[6]v", -1)
formatStr = strings.Replace(formatStr, "{balance}", "%[8]v", -1)
} else {
switch {
case lastBlock:
sort.Slice(list, func(i, j int) bool { return sealers[list[i]].LastBlock > sealers[list[j]].LastBlock })
case timestamp:
sort.Slice(list, func(i, j int) bool { return sealers[list[i]].Time > sealers[list[j]].Time })
case difficulty:
sort.Slice(list, func(i, j int) bool { return sealers[list[i]].Difficulty > sealers[list[j]].Difficulty })
case balance:
sort.Slice(list, func(i, j int) bool { return sealers[list[i]].Balance.Cmp(sealers[list[j]].Balance.Int) > 0 })
}
formatStr = "%42v"
if lastBlock {
formatStr += " %[2]*[3]v"
lastBlockLen = util.Max(2, int64(len(strconv.FormatInt(sealers[list[0]].LastBlock, 10))))
}
if timestamp {
formatStr += " %[4]*[5]v"
}
if difficulty {
formatStr += " %2[6]v"
}
headerFmt = formatStr
if balance {
formatStr += " %#12.6[8]e"
headerFmt += " %[7]*[8]v"
balanceLen = 12
}
if header {
headerFmt += "\n"
fmt.Printf(headerFmt,
fmt.Sprintf("%-24v", "Sealer"),
lastBlockLen, fmt.Sprintf("%*v", -lastBlockLen/2-2, "Last"),
timestampLen, fmt.Sprintf("%*v", -timestampLen/2-2, "Time"),
"Dif",
balanceLen, fmt.Sprintf("%*v", -(int(balanceLen)+len(unit))/2-1, strings.Title(unit)))
}
}
formatStr += "\n"
for _, sealer := range list {
var formatedTimestamp interface{}
s := sealers[sealer]
t := int64(s.Time)
formatedTimestamp = t
if len(format) > 0 && t > 0 {
formatedTimestamp = time.Unix(t, 0).Format(format)
}
convertedBalance, _ := weiTo(s.Balance.Int, unit)
fmt.Printf(formatStr, sealer.Hex(), lastBlockLen, s.LastBlock, timestampLen, formatedTimestamp, s.Difficulty, balanceLen, convertedBalance)
}
}
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(false)
util.Ensure(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.Ensure(node.IsSealer(bfa.Self), "Solo los selladores pueden votar")
votes := node.Votes(latest)
genesisSigners := node.SealersAtBlock(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++
}
}
util.Ensure(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 := proposal.In(votes.Signers)
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 proposal.In(genesisSigners) && 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.String()] = !isSealer
} else {
fmt.Printf("Voto por %v: %v\n", proposal, !isSealer)
}
}
if json {
util.PrintJson(voted)
}
}
func propose() {
var (
authorize bool
voted = make(map[string]bool)
)
description = "Vota por una propuesta."
otherArgs = "[propuesta...]"
setFlags()
flags.BoolVar(&authorize, "authorize", true, "Sentido del voto (true: a favor, false: en contra).")
parseFlags(true)
url = updateURL(url)
node, err := bfa.Dial(url)
util.Check(err)
defer node.Close()
util.Ensure(node.IsSealer(bfa.Self), "Solo los selladores pueden votar")
votes := node.Votes(latest)
util.Ensure(flags.NArg() > 0, "No se especificaron candidatos por los cuales votar\n")
for i := 0; i < flags.NArg(); i++ {
addr := flags.Arg(i)
util.Ensure(util.IsValidAddress(addr), "'%v' no es una dirección válida\n", addr)
address := bfa.HexToAddress(addr)
if _, ok := votes.Tally[address]; !ok {
isSealer := address.In(votes.Signers)
switch { // address is not in a proposal, we only allow removing signers or adding non signers
case isSealer && authorize:
util.Error("'%v' ya es un sellador\n", address)
case !isSealer && !authorize:
util.Error("'%v' no es un sellador\n", address)
}
}
node.Propose(address, authorize)
if json {
voted[address.String()] = authorize
} else {
fmt.Printf("Voto por %v: %v\n", address, authorize)
}
}
if json {
util.PrintJson(voted)
}
}
func status() {
nodeStatus := struct {
Enode string `json:"enode"`
Network uint64 `json:"network"`
Genesis string `json:"genesis"`
BFANetwork string `json:"bfaNetwork"`
BFAGenesis string `json:"bfaGenesis"`
Accounts map[bfa.Address]*bfa.BigInt `json:"accounts"`
Coinbase bfa.Address `json:"coinbase"`
IsSealer bool `json:"isSealer"`
IsMining bool `json:"isMining"`
BlockNumber int64 `json:"blockNumber"`
Time int64 `json:"timestamp"`
DateTime string `json:"datetime"`
LastBlockSigned int64 `json:"lastBlockSigned,omitempty"`
LastTimeSigned int64 `json:"lastTimeSigned,omitempty"`
DateTimeLastBlockSigned string `json:"datetimeLastSigned,omitempty"`
PeerCount uint64 `json:"peerCount"`
}{
Accounts: make(map[bfa.Address]*bfa.BigInt),
}
description = "Muestra el estado del nodo."
setFlags()
parseFlags(false)
url = updateURL(url)
node, err := bfa.Dial(url)
util.Check(err)
defer node.Close()
for _, account := range node.Accounts() {
nodeStatus.Accounts[account] = node.Balance(account)
}
nodeStatus.Coinbase, _ = node.Coinbase()
nodeStatus.BlockNumber = node.BlockNumber()
header := node.HeaderByNumber(nodeStatus.BlockNumber)
nodeStatus.Time = int64(header.Time)
nodeStatus.DateTime = time.Unix(nodeStatus.Time, 0).Format("2006-01-02 15:04:05")
nodeStatus.IsSealer = node.IsSealer(bfa.Self)
nodeStatus.IsMining = node.IsMining()
if nodeStatus.IsSealer {
nodeStatus.LastBlockSigned = node.LastBlockSignedBy(nodeStatus.Coinbase, nodeStatus.BlockNumber)
if nodeStatus.LastBlockSigned != 0 {
header := node.HeaderByNumber(nodeStatus.LastBlockSigned)
nodeStatus.LastTimeSigned = int64(header.Time)
nodeStatus.DateTimeLastBlockSigned = time.Unix(nodeStatus.LastTimeSigned, 0).Format("2006-01-02 15:04:05")
}
}
nodeInfo := node.NodeInfo()
nodeStatus.Enode = nodeInfo.Enode
ethInfo := nodeInfo.Protocols["eth"].(map[string]interface{})
nodeStatus.Genesis = ethInfo["genesis"].(string)
nodeStatus.Network = uint64(ethInfo["network"].(float64))
nodeStatus.BFAGenesis = bfa.Genesis(nodeStatus.Genesis).String()
nodeStatus.BFANetwork = bfa.Network(nodeStatus.Network).String()
nodeStatus.PeerCount = node.PeerCount()
util.PrintJson(nodeStatus)
}
func block() {
var (
blockNumber int64
)
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)")
parseFlags(false)
url = updateURL(url)
node, err := bfa.Dial(url)
util.Check(err)
defer node.Close()
blockNumber = node.BlockNumberInRange(blockNumber)
block := node.BlockByNumber(blockNumber)
util.PrintJson(block)
}
func snapshot() {
var blockNumber int64
description = "Muestra el snapshot en un bloque"
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)")
parseFlags(false)
url = updateURL(url)
node, err := bfa.Dial(url)
util.Check(err)
defer node.Close()
blockNumber = node.BlockNumberInRange(blockNumber)
snapshot := node.SnapshotAtBlock(blockNumber)
util.PrintJson(snapshot)
}
func transfers() {
type Transfer struct {
From bfa.Address `json:"from"`
To bfa.Address `json:"to"`
Amount *bfa.BigInt `json:"amount"`
BlockNumber int64 `json:"blockNumber"`
}
var (
first, last, end int64
)
description = "Muestra transferencias en un rango de bloques, y opcionalmente, restringidas a ciertas direcciones."
otherArgs = "[dirección...]"
setFlags()
flags.Int64Var(&first, "first-block", latest, "Primer bloque del rango (-1 para especificar el ú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()
set := make(map[bfa.Address]bool)
txs := make([]Transfer, 0)
for i := 0; i < flags.NArg(); i++ {
address := flags.Arg(i)
util.Ensure(util.IsValidAddress(address), "'%v' no es una dirección válida\n", address)
set[bfa.HexToAddress(address)] = true
}
latest := node.BlockNumber()
if first < 0 {
first = latest
}
if last < 0 || last > latest {
end = latest + 1
} else {
end = last + 1
}
for first < end {
for i := first; i < end; i++ {
if node.BlockTransactionCount(i) > 0 {
block := node.BlockByNumber(i)
for _, transaction := range block.Transactions {
if !transaction.Value.IsZero() {
src := transaction.From
dst := transaction.To
if len(set) == 0 || set[src] || set[dst] {
if json {
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)
}
}
}
}
}
}
first = end
if last < 0 || last > end {
latest = node.BlockNumber()
if latest < last {
end = latest + 1
} else {
end = last + 1
}
}
}
if json {
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
}
var (
first, last, end, lastBlock int64
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)")
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()
sealerStats = make(map[bfa.Address]*Stats)
latest := node.BlockNumber()
if last < 0 || last > latest {
last = latest
}
end = last + 1
if first <= 0 && first < last {
first = last + first
}
lastBlock = first - 1
first += int64(len(node.SnapshotAtBlock(first).Recents))
if first > last {
first = last
}
for first < end {
for sealer := range node.SnapshotAtBlock(end - 1).Signers {
if _, ok := sealerStats[sealer]; !ok {
sealerStats[sealer] = &Stats{}
}
}
for block := first; block < end; {
snapshot := node.SnapshotAtBlock(block)
recentsLength := int64(len(snapshot.Recents))
shouldHaveSealedAfter := block - 4*recentsLength
if shouldHaveSealedAfter < 0 {
shouldHaveSealedAfter = 0
}
if block-recentsLength > lastBlock {
fmt.Fprintln(os.Stderr, "Perdimos un sellador, retrocediendo")
block--
continue
}
var blocksProcessed int64 = 0
for blockNumber, sealer := range snapshot.Recents {
blockNumber := int64(blockNumber)
if blockNumber <= lastBlock {
continue
}
blocksProcessed++
if stats, ok := sealerStats[sealer]; ok {
// We are interested in this sealer
stats.LastBlockSealed = blockNumber
if stats.FirstBlockSealed == 0 {
stats.FirstBlockSealed = blockNumber
}
stats.BlocksSealed++
stats.recent = true
}
}
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 {
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
} else {
stats.BlocksAlive = 1
}
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
block += recentsLength
}
first = end
if last < 0 || last > end {
latest = node.BlockNumber()
if latest < last {
end = latest + 1
} else {
end = last + 1
}
}
}
for _, stats := range sealerStats {
if stats.LastSeen == 0 {
continue
}
blocksSeen := stats.LastSeen - stats.FirstSeen + 1
stats.Availability = float64(stats.BlocksAlive) / float64(blocksSeen)
}
util.PrintJson(sealerStats)
}
func main() {
var (
commands = map[string]func(){
"proposals": proposals,
"sealers": sealers,
"vote": propose,
"autovote": autovote,
"status": status,
"block": block,
"snapshot": snapshot,
"transfers": transfers,
"sealerstats": sealerstats,
}
validCommands []string
command func()
)
for cmd := range commands {
validCommands = append(validCommands, cmd)
}
sort.Strings(validCommands)
if len(os.Args) > 1 {
command = commands[os.Args[1]]
}
util.Ensure(command != nil, "Uso: %v <%v> [opciones]\nPara ayuda: %v <command> -h\n", path.Base(os.Args[0]), strings.Join(validCommands, "|"), path.Base(os.Args[0]))
command()
}
go.mod 0 → 100644
go.sum 0 → 100644
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw=
github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847 h1:rtI0fD4oG/8eVokGVPYJEW1F88p1ZNgXiEIs9thEE4A=
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ=
github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X6GqbFowYdYdI0L9bwxL07jyPZIdepyZ0=
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/ethereum/go-ethereum v1.9.23 h1:SIKhg/z4Q7AbvqcxuPYvMxf36che/Rq/Pp0IdYEkbtw=
github.com/ethereum/go-ethereum v1.9.23/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM=
github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26 h1:lMm2hD9Fy0ynom5+85/pbdkiYcBqM1JWmhpAXLmy0fw=
github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989 h1:giknQ4mEuDFmmHSrGcbargOuLHQGtywqo4mheITex54=
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo=
github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc=
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c h1:1RHs3tNxjXGHeul8z2t6H2N2TlAqpKe5yryJztRx4Jk=
github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150 h1:ZeU+auZj1iNzN8iVhff6M38Mfu73FQiJve/GEXYJBjE=
github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v2.20.5+incompatible h1:tYH07UPoQt0OCQdgWWMgYHy3/a9bcxNpBIysykNIP7I=
github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw=
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM=
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk=
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8 h1:AvbQYmiaaaza3cW3QXRyPo5kYgpFIzOAfeAAN7m3qQ4=
golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
package bfa
import (
"github.com/ethereum/go-ethereum/common"
"sort"
)
type Address struct{ common.Address }
func (address Address) MarshalText() ([]byte, error) {
return []byte(address.Hex()), nil
}
func (a Address) LessThan(b Address) bool {
for i, x := range a.Address {
if x >= b.Address[i] {
return false
}
}
return true
}
func (address Address) In(slice []Address) bool {
for _, x := range slice {
if x == address {
return true
}
}
return false
}
func SortAddresses(slice []Address) {
sort.Slice(slice, func(i int, j int) bool { return slice[i].LessThan(slice[j]) })
}
func HexToAddress(s string) (address Address) {
address = Address{common.HexToAddress(s)}
return
}
package bfa
import (
"bytes"
"encoding/hex"
"errors"
"math/big"
"strconv"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"gitlab.bfa.ar/miguel/bfa/internal/util"
)
type BigInt struct {
*big.Int
}
func (bigInt *BigInt) UnmarshalJSON(b []byte) error {
if i, ok := new(big.Int).SetString(string(b[1:len(b)-1]), 0); ok {
*bigInt = BigInt{i}
return nil
}
return errors.New("can't unmarshal BigInt")
}
func (bigInt *BigInt) IsZero() bool {
return bigInt.BitLen() == 0
}
type Uint64 uint64
func (u *Uint64) UnmarshalJSON(b []byte) (err error) {
i, err := strconv.ParseUint(string(b[1:len(b)-1]), 0, 64)
*u = Uint64(i)
return
}
type Bytes []byte
func (b Bytes) MarshalJSON() ([]byte, error) {
dest := make([]byte, 2+hex.EncodedLen(len(b)))
copy(dest, []byte("\"0x"))
hex.Encode(dest[2:], b)
dest[len(dest)-1] = '"'
return dest, nil
}
func (b *Bytes) UnmarshalJSON(src []byte) (err error) {
util.Require(len(src) >= 4 && bytes.Equal(src[1:3], []byte("0x")), "invalid json string")
dest := make([]byte, hex.DecodedLen(len(src)-4))
_, err = hex.Decode(dest, src[3:len(src)-1])
if err == nil {
*b = dest
}
return
}
// RPCTransaction represents a transaction
type RPCTransaction struct {
BlockHash common.Hash `json:"blockHash"`
BlockNumber Uint64 `json:"blockNumber"`
From Address `json:"from"`
Gas Uint64 `json:"gas"`
GasPrice *BigInt `json:"gasPrice"`
Hash common.Hash `json:"hash"`
Input string `json:"input"`
Nonce Uint64 `json:"nonce"`
To Address `json:"to"`
TransactionIndex Uint64 `json:"transactionIndex"`
Value *BigInt `json:"value"`
V *BigInt `json:"v"`
R *BigInt `json:"r"`
S *BigInt `json:"s"`
}
type Header struct {
ParentHash common.Hash `json:"parentHash"`
UncleHash common.Hash `json:"sha3Uncles"`
Coinbase Address `json:"miner"`
Root common.Hash `json:"stateRoot"`
TxHash common.Hash `json:"transactionsRoot"`
ReceiptHash common.Hash `json:"receiptsRoot"`
Bloom types.Bloom `json:"logsBloom"`
Difficulty *BigInt `json:"difficulty"`
Number *BigInt `json:"number"`
GasLimit Uint64 `json:"gasLimit"`
GasUsed Uint64 `json:"gasUsed"`
Time Uint64 `json:"timestamp"`
Extra Bytes `json:"extraData"`
MixDigest common.Hash `json:"mixHash"`
Nonce types.BlockNonce `json:"nonce"`
}
func (header *Header) ExtraWithoutSignature() []byte {
return header.Extra[:len(header.Extra)-65]
}
func (header *Header) Signature() []byte {
return header.Extra[len(header.Extra)-65:]
}
func (header *Header) ethHeader() *types.Header {
var h types.Header
h.ParentHash = header.ParentHash
h.UncleHash = header.UncleHash
h.Coinbase = header.Coinbase.Address
h.Root = header.Root
h.TxHash = header.TxHash
h.ReceiptHash = header.ReceiptHash
h.Bloom = header.Bloom
h.Difficulty = header.Difficulty.Int
h.Number = header.Number.Int
h.GasLimit = uint64(header.GasLimit)
h.GasUsed = uint64(header.GasUsed)
h.Time = uint64(header.Time)
h.Extra = header.Extra
h.MixDigest = header.MixDigest
h.Nonce = header.Nonce
return &h
}
func (header *Header) GetSigner() Address {
return getSigner(header.ethHeader())
}
func (header *Header) GetHash() (h common.Hash) {
headerBytes, _ := rlp.EncodeToBytes(header.ethHeader())
return crypto.Keccak256Hash(headerBytes)
}
type Block struct {
Header
Transactions []RPCTransaction `json:"transactions"`
TotalDifficulty Uint64 `json:"totalDifficulty"`
Signer Address `json:"signer"`
Hash common.Hash `json:"hash"`
}
package bfa
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
)
func sigHash(header *types.Header) (hash common.Hash) {
headerBytes, _ := rlp.EncodeToBytes([]interface{}{
header.ParentHash,
header.UncleHash,
header.Coinbase,
header.Root,
header.TxHash,
header.ReceiptHash,
header.Bloom,
header.Difficulty,
header.Number,
header.GasLimit,
header.GasUsed,
header.Time,
header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
header.MixDigest,
header.Nonce,
})
return crypto.Keccak256Hash(headerBytes)
}
func getSigner(header *types.Header) (address Address) {
if header.Number.Int64() == 0 { // Genesis block has no signers, we return an empty address
return
}
hash := sigHash(header).Bytes()
pubkey, err := crypto.Ecrecover(hash, header.Extra[len(header.Extra)-65:])
if err != nil {
panic(err)
}
copy(address.Address[:], crypto.Keccak256(pubkey[1:])[12:])
return
}
package bfa
import (
"strconv"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
. "gitlab.bfa.ar/miguel/bfa/internal/util"
)
type Node rpc.Client
type Network int
type Genesis string
type Vote struct {
Signer Address `json:"signer"` // Authorized signer that cast this vote
Block uint64 `json:"block"` // Block number the vote was cast in (expire old votes)
Address Address `json:"address"` // Account being voted on to change its authorization
Authorize bool `json:"authorize"` // Whether to authorize or deauthorize the voted account
}
type Snapshot struct {
Number uint64 `json:"number"` // Block number where the snapshot was created
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
Signers map[Address]struct{} `json:"signers"` // Set of authorized signers at this moment
Recents map[uint64]Address `json:"recents"` // Set of recent signers for spam protections
Votes []Vote `json:"votes"` // List of votes cast in chronological order
Tally map[Address]clique.Tally `json:"tally"` // Current vote tally to avoid recalculating
}
type Tally struct {
True int `json:"true"`
False int `json:"false"`
Null int `json:"null"`
}
type Proposals struct {
BlockNumber int64 `json:"number"` // Block number where the snapshot was created
Proposals []Address `json:"proposals"` // List of proposals being voted
Signers []Address `json:"signers"` // List of authorized signers at this moment
Tally map[Address]*Tally `json:"tally"` // Count of positive, negative and empty votes for a proposal
Votes map[Address]map[Address]*bool `json:"votes"` // List of votes for each proposal
}
type SealerStatus struct {
LastBlock int64 `json:"lastBlockSigned"`
Time uint64 `json:"timestamp"`
Difficulty uint64 `json:"difficulty"`
Balance *BigInt `json:"balance"`
}
const (
Latest = "latest"
MainNetwork Network = 47525974938
MainGenesis Genesis = "0xe0e26f415af00fe32e1e8d94336355db43a78d9f59eb70910172b61fb670d99e"
TestNetwork Network = 55555000000
TestGenesis Genesis = "0x67e3e0fe207bd536875c14293132029b2cbf72aeb47afa865c2262293bca7d58"
SealerRounds = 2
)
func (network Network) String() string {
switch network {
case MainNetwork:
return "main"
case TestNetwork:
return "test"
default:
return "unknown"
}
}
func (genesis Genesis) String() string {
switch genesis {
case MainGenesis:
return "main"
case TestGenesis:
return "test"
default:
return "unknown"
}
}
var Self = Address{} // Empty address to represent the address of the caller
func (node *Node) Call(result interface{}, method string, args ...interface{}) {
Check((*rpc.Client)(node).Call(result, method, args...))
}
func (node *Node) CallWithError(result interface{}, method string, args ...interface{}) error {
return (*rpc.Client)(node).Call(result, method, args...)
}
func (node *Node) BlockNumber() int64 {
var bn rpc.BlockNumber
node.Call(&bn, "eth_blockNumber")
return bn.Int64()
}
func (node *Node) GasPrice() (gp int64) {
node.Call(&gp, "eth_gasPrice")
return gp
}
func (node *Node) Accounts() (accounts []Address) {
node.Call(&accounts, "eth_accounts")
return
}
func (node *Node) IsMining() (mining bool) {
node.Call(&mining, "eth_mining")
return
}
func (node *Node) PeerCount() uint64 {
var peerCount Uint64
node.Call(&peerCount, "net_peerCount")
return uint64(peerCount)
}
func (node *Node) BalanceAtBlock(account Address, blockNumber int64) *BigInt {
var balance BigInt
node.Call(&balance, "eth_getBalance", account, hexBlockNumber(blockNumber))
return &balance
}
func (node *Node) Balance(account Address) *BigInt {
return node.BalanceAtBlock(account, -1)
}
func (node *Node) Coinbase() (address Address, err error) {
err = node.CallWithError(&address, "eth_coinbase")
return
}
func (node *Node) BlockNumberInRange(number int64) (blockNumber int64) {
latest := node.BlockNumber()
switch {
case number == -1, number > latest:
blockNumber = latest
case number < 0:
blockNumber = Max(0, latest+1+number)
default:
blockNumber = number
}
return
}
func (node *Node) HeaderByNumber(blockNumber int64) (header Header) {
node.Call(&header, "eth_getBlockByNumber", hexBlockNumber(blockNumber), false)
return
}
func (node *Node) HeaderByHash(hash common.Hash) (header Header) {
node.Call(&header, "eth_getBlockByHash", hash, false)
return
}
func (node *Node) BlockByNumber(blockNumber int64) (block Block) {
node.Call(&block, "eth_getBlockByNumber", hexBlockNumber(blockNumber), true)
block.Signer = block.GetSigner()
block.Hash = block.GetHash()
return
}
func (node *Node) BlockByHash(hash common.Hash) (block Block) {
node.Call(&block, "eth_getBlockByHash", hash, true)
block.Signer = block.GetSigner()
block.Hash = block.GetHash()
return
}
func (node *Node) BlockTransactionCount(blockNumber int64) (count int64) {
var num hexutil.Uint64
node.Call(&num, "eth_getBlockTransactionCountByNumber", hexBlockNumber(blockNumber))
return int64(num)
}
func (node *Node) BlockSigner(blockNumber int64) (signer Address) {
if blockNumber == 0 { // we return an empty signer for genesis block
return
}
header := node.HeaderByNumber(blockNumber)
return header.GetSigner()
}
func (node *Node) GetSnapshot() (snapshot Snapshot) {
node.Call(&snapshot, "clique_getSnapshot", nil)
return
}
func (node *Node) SnapshotAtBlock(blockNumber int64) (snapshot Snapshot) {
node.Call(&snapshot, "clique_getSnapshot", hexBlockNumber(blockNumber))
return
}
func (node *Node) Sealers() (signers []Address) {
node.Call(&signers, "clique_getSigners", nil)
return
}
func (node *Node) NodeInfo() (nodeInfo p2p.NodeInfo) {
node.Call(&nodeInfo, "admin_nodeInfo", nil)
return
}
func (node *Node) SealersAtBlock(blockNumber int64) (signers []Address) {
if blockNumber == -1 {
return node.Sealers()
}
node.Call(&signers, "clique_getSigners", Int64ToHex(blockNumber))
return
}
func (node *Node) IsSealer(address Address) bool {
if address == Self {
var (
err error
)
if address, err = node.Coinbase(); err != nil {
return false
}
}
return address.In(node.Sealers())
}
func (node *Node) IsSealerAtBlock(address Address, blockNumber int64) bool {
if address == Self {
var (
err error
)
if address, err = node.Coinbase(); err != nil {
return false
}
}
return address.In(node.SealersAtBlock(blockNumber))
}
func (node *Node) Propose(address Address, vote bool) {
node.Call(nil, "clique_propose", address.String(), vote)
return
}
func (node *Node) Votes(blockNumber int64) (votes Proposals) {
var (
snapshot Snapshot
)
if blockNumber < 0 {
snapshot = node.GetSnapshot()
} else {
snapshot = node.SnapshotAtBlock(blockNumber)
}
votes.BlockNumber = int64(snapshot.Number)
for signer := range snapshot.Signers {
votes.Signers = append(votes.Signers, signer)
}
SortAddresses(votes.Signers)
for proposal := range snapshot.Tally {
votes.Proposals = append(votes.Proposals, proposal)
}
SortAddresses(votes.Proposals)
votes.Votes = make(map[Address]map[Address]*bool)
votes.Tally = make(map[Address]*Tally)
for _, v := range snapshot.Votes {
proposal := v.Address
signer := v.Signer
if votes.Votes[proposal] == nil {
votes.Votes[proposal] = make(map[Address]*bool)
for _, signer := range votes.Signers {
votes.Votes[proposal][signer] = nil
}
votes.Tally[proposal] = &Tally{0, 0, len(votes.Signers)}
}
votes.Votes[proposal][signer] = &v.Authorize
if v.Authorize {
votes.Tally[proposal].True += 1
} else {
votes.Tally[proposal].False += 1
}
votes.Tally[proposal].Null -= 1
}
return
}
func (node *Node) LastBlockSignedBy(address Address, upperLimit int64) (blockNumber int64) {
if upperLimit == 0 || !node.IsSealer(address) {
return
}
for i := 0; i < 5; i++ {
snapshot := node.SnapshotAtBlock(upperLimit)
for number, sealer := range snapshot.Recents {
if sealer == address {
return int64(number)
}
}
upperLimit = int64(snapshot.Number) - int64((len(snapshot.Recents)))
if upperLimit <= 0 {
return
}
}
return
}
func (node *Node) SealersStatus(blockNumber int64) (status map[Address]*SealerStatus) {
if blockNumber == 0 { // Genesis block doesn't have signer
return
}
status = make(map[Address]*SealerStatus)
for _, address := range node.SealersAtBlock(blockNumber) {
status[address] = &SealerStatus{
Balance: node.BalanceAtBlock(address, blockNumber),
}
}
notSeen := int64(len(status))
block := node.HeaderByNumber(blockNumber)
blockNumber = block.Number.Int64()
until := Max(1, blockNumber-SealerRounds*notSeen)
for notSeen > 0 {
signer := block.GetSigner()
if status[signer].LastBlock == 0 {
status[signer].LastBlock = block.Number.Int64()
status[signer].Time = uint64(block.Time)
status[signer].Difficulty = block.Difficulty.Uint64()
notSeen--
}
if blockNumber == until {
break
}
blockNumber--
block = node.HeaderByNumber(blockNumber)
}
return status
}
func Dial(url string) (*Node, error) {
client, err := rpc.Dial(url)
return (*Node)(client), err
}
func (node *Node) Close() {
(*rpc.Client)(node).Close()
}
func hexBlockNumber(number int64) (blockNumber string) {
if number == -1 {
blockNumber = Latest
} else {
blockNumber = "0x" + strconv.FormatInt(number, 16)
}
return
}
package bfa
import (
"flag"
"github.com/ethereum/go-ethereum/common"
"log"
"os"
"testing"
)
const (
NumBlocks int64 = 1000
)
var (
node *Node
numbers map[string]int64
hashes map[string][]common.Hash
)
func BenchmarkBlockByNumber(b *testing.B) {
for r, base := range numbers {
b.Run(r, func(b *testing.B) {
for i := int64(0); i < int64(b.N); i++ {
_ = node.BlockByNumber(base + i%NumBlocks)
}
})
}
}
func BenchmarkHeaderByNumber(b *testing.B) {
for r, base := range numbers {
b.Run(r, func(b *testing.B) {
for i := int64(0); i < int64(b.N); i++ {
_ = node.HeaderByNumber(base + i%NumBlocks)
}
})
}
}
func BenchmarkBlockByHash(b *testing.B) {
for r := range numbers {
b.Run(r, func(b *testing.B) {
for i := int64(0); i < int64(b.N); i++ {
_ = node.BlockByHash(hashes[r][i%NumBlocks])
}
})
}
}
func BenchmarkHeaderByHash(b *testing.B) {
for r := range numbers {
b.Run(r, func(b *testing.B) {
for i := int64(0); i < int64(b.N); i++ {
_ = node.HeaderByHash(hashes[r][i%NumBlocks])
}
})
}
}
func (node *Node) getBlocksByNumber(last int64, n int64) int64 {
block := node.BlockByNumber(last)
for i := int64(0); i < n; i++ {
block = node.BlockByNumber(block.Number.Int64() - 1)
}
return block.Number.Int64()
}
func (node *Node) getBlocksByHash(last int64, n int64) int64 {
block := node.BlockByNumber(last)
for i := int64(0); i < n; i++ {
block = node.BlockByHash(block.ParentHash)
}
return block.Number.Int64()
}
func (node *Node) getHeadersByNumber(last int64, n int64) int64 {
header := node.HeaderByNumber(last)
for i := int64(0); i < n; i++ {
header = node.HeaderByNumber(header.Number.Int64() - 1)
}
return header.Number.Int64()
}
func (node *Node) getHeadersByHash(last int64, n int64) int64 {
header := node.HeaderByNumber(last)
for i := int64(0); i < n; i++ {
header = node.HeaderByHash(header.ParentHash)
}
return header.Number.Int64()
}
func TestBlockGetters(t *testing.T) {
latest := node.BlockNumber()
if latest < NumBlocks {
t.Skip("No hay suficientes bloques")
}
t.Run("BlockByNumber", func(t *testing.T) {
if node.getBlocksByNumber(latest, NumBlocks) != latest-NumBlocks {
t.Fail()
}
})
t.Run("BlockByHash", func(t *testing.T) {
if node.getBlocksByHash(latest, NumBlocks) != latest-NumBlocks {
t.Fail()
}
})
}
func TestHeaderGetters(t *testing.T) {
latest := node.BlockNumber()
if latest < NumBlocks {
t.Skip("No hay suficientes bloques")
}
t.Run("HeaderByNumber", func(t *testing.T) {
if node.getHeadersByNumber(latest, NumBlocks) != latest-NumBlocks {
t.Fail()
}
})
t.Run("HeaderByHash", func(t *testing.T) {
if node.getHeadersByHash(latest, NumBlocks) != latest-NumBlocks {
t.Fail()
}
})
}
func BenchmarkBlockGetters(b *testing.B) {
latest := node.BlockNumber()
if latest < NumBlocks {
b.Skip("No hay suficientes bloques")
}
b.Run("BlockByNumber", func(b *testing.B) {
for i := int64(0); i < int64(b.N); i++ {
_ = node.getBlocksByNumber(latest, NumBlocks)
}
})
b.Run("BlockByHash", func(b *testing.B) {
for i := int64(0); i < int64(b.N); i++ {
_ = node.getBlocksByHash(latest, NumBlocks)
}
})
}
func BenchmarkHeaderGetters(b *testing.B) {
latest := node.BlockNumber()
if latest < NumBlocks {
b.Skip("No hay suficientes bloques")
}
b.Run("HeaderByNumber", func(b *testing.B) {
for i := int64(0); i < int64(b.N); i++ {
_ = node.getHeadersByNumber(latest, NumBlocks)
}
})
b.Run("HeaderByHash", func(b *testing.B) {
for i := int64(0); i < int64(b.N); i++ {
_ = node.getHeadersByHash(latest, NumBlocks)
}
})
}
func TestMain(m *testing.M) {
flag.Parse()
var err error
if node, err = Dial("http://localhost:8545"); err != nil {
log.Fatal(err)
}
latest := node.BlockNumber()
if latest < 3*NumBlocks {
log.Fatal("No hay suficientes bloques como para correr benchmarks")
}
numbers = map[string]int64{"lo": 1, "mid": latest / 2, "hi": latest - NumBlocks}
hashes = make(map[string][]common.Hash)
for r, base := range numbers {
for i := int64(0); i < NumBlocks; i++ {
header := node.HeaderByNumber(base + 1)
hashes[r] = append(hashes[r], header.GetHash())
}
}
os.Exit(m.Run())
}
package util
import (
"encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/common"
"log"
"os"
"runtime"
"strconv"
"strings"
)
func Contains(slice []string, s string) bool {
for _, x := range slice {
if x == s {
return true
}
}
return false
}
func Error(format string, args ...interface{}) {
_, _ = fmt.Fprintf(os.Stderr, format, args...)
os.Exit(1)
}
func Ensure(condition bool, format string, args ...interface{}) {
if !condition {
Error(format, args...)
}
}
func Check(err error) {
Ensure(err == nil, "%v\n", err)
}
func Int64ToHex(n int64) string {
return "0x" + strconv.FormatInt(n, 16)
}
func Require(condition bool, msg string) {
if !condition {
ptr, _, _, _ := runtime.Caller(1)
log.Panicf("%v in %v", msg, runtime.FuncForPC(ptr).Name())
}
}
func Min(a, b int64) int64 {
if a < b {
return a
}
return b
}
func Max(a, b int64) int64 {
if a > b {
return a
}
return b
}
func PrintJson(s interface{}) {
v, err := json.MarshalIndent(s, "", " ")
Check(err)
fmt.Println(string(v))
return
}
func isMixedCase(address string) bool {
return strings.ContainsAny(address, "abcdef") && strings.ContainsAny(address, "ABCDEF")
}
func IsValidAddress(address string) bool {
if !common.IsHexAddress(address) {
return false
}
if isMixedCase(address) {
return common.HexToAddress(address).Hex() == address
} else {
return true
}
}
package util
import (
"strings"
"testing"
)
var (
addresses = map[string]string{
"ValidReal": "0x46991ada2a2544468eb3673524641bf293f23ccc",
"ValidSeq": "0x000102030405060708090a0b0c0d0e0f10111213",
"ValidZero": "0x0000000000000000000000000000000000000000",
"InvalidNoPrefix": "0046991ada2a2544468eb3673524641bf293f23ccc",
"InvalidEmpty": "",
"InvalidLengthVeryShort": "0x",
"InvalidLengthShort": "0x46991ada2a2544468eb3673524641bf293f23cc",
"InvalidLengthLong": "0x46991ada2a2544468eb3673524641bf293f23ccc00",
"InvalidNoHexAtBeginning": "0xx6991ada2a2544468eb3673524641bf293f23ccc",
"InvalidNoHexAtEnd": "0x46991ada2a2544468eb3673524641bf293f23ccx",
}
)
func TestValidAddress(t *testing.T) {
for name, address := range addresses {
t.Run(name, func(t *testing.T) {
if IsValidAddress(address) != strings.HasPrefix(name, "Valid") {
t.Fail()
}
})
}
}
func BenchmarkIsAddressTrue(b *testing.B) {
for name, address := range addresses {
b.Run(name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = IsValidAddress(address)
}
})
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment