Skip to content
Snippets Groups Projects
bfa_client.go 6.11 KiB
Newer Older
package main

import (
	"../bfa"
	"../util"
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
)

const (
	latest = -1
type Node = bfa.Node
var (
	url         string
	json        bool
	help        bool
	flags       flag.FlagSet
	command     string
	description string
	otherArgs   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
		return
	}
	// 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() {
	flags.Parse(os.Args[2:])
	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)
	}
}

func sealers() {
	var (
		blockNumber int64
		status      bool
		timestamp   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(&timestamp, "timestamp", false, "Muestra el timestamp del sellado en lugar del número de bloque.")
	parseFlags()
	if blockNumber == 0 {
		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))))
		}
		for _, sealer := range list {
			var output interface{}
			if timestamp {
				output = sealers[sealer].Time
			} else {
				output = sealers[sealer].LastBlock
			}
			fmt.Printf("%v: %*d\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)
			}
		}
	}
func propose() {
	var (
		authorize bool
		proposals []string
	)
	description = "Vota por una propuesta."
	otherArgs = "[propuesta...]"
	setFlags()
	flags.BoolVar(&all, "all", false, "Vota en todas las propuestas activas")
	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")
	if all {
		votes := node.GetVotes(latest)
		for _, proposal := range votes.Proposals {
			proposals = append(proposals, proposal)
		}
		if flags.NArg() != 0 {
			fmt.Fprintf(os.Stderr, "Se especificó -all. Ignorando argumentos adicionales.")
		}
	} else {
		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))
			}
			isSealer := node.IsSealer(address)
			switch {
			case isSealer && authorize:
				panic(fmt.Sprintf("'%v' ya es un sellador", address))
			case !isSealer && !authorize:
				panic(fmt.Sprintf("'%v' no es un sellador", address))
			proposals = append(proposals, flags.Arg(i))
		}
	for _, proposal := range proposals {
		node.Propose(proposal, authorize)
		fmt.Printf("Voto por %v: %v\n", proposal, authorize)
		commands = map[string]func(){
			"proposals": proposals,
			"sealers":   sealers,
			"vote":      propose,
		validCommands []string
	for command := range commands {
		validCommands = append(validCommands, command)
	}
	defer func() {
		if err := recover(); err != nil {
			log.Printf("Error: %s", err)
			usage(1)
		}
	}()
	if len(os.Args) > 1 {
		command = os.Args[1]
	}
	if commands[command] == nil {
		fmt.Fprintf(os.Stderr, "Uso: %v <%v> [opciones]\n", path.Base(os.Args[0]), strings.Join(validCommands, "|"))
		fmt.Fprintf(os.Stderr, "Para ayuda: %v <command> -h\n", path.Base(os.Args[0]))
		os.Exit(1)
	}
	commands[command]()