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

Agregado código de votación

parent 13aeb83f
No related branches found
No related tags found
No related merge requests found
SRC:=src/client/bfa_client.go src/bfa/node.go src/util/util.go src/clique/clique.go
all: bin/bfa_client all: bin/bfa_client
bin/bfa_client: src/client/bfa_client.go | bin bin/bfa_client: $(SRC) | bin
go build -o $@ $< go build -o $@ $<
bin: bin:
......
package bfa package bfa
import ( import (
. "../clique"
. "../util" . "../util"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"sort" "sort"
"strconv"
) )
type Node rpc.Client type Node rpc.Client
...@@ -47,30 +44,6 @@ type SealerInfo struct { ...@@ -47,30 +44,6 @@ type SealerInfo struct {
LastBlock int64 LastBlock int64
} }
func sigHash(header *types.Header) (hash common.Hash) {
hasher := sha3.NewKeccak256()
rlp.Encode(hasher, []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,
})
hasher.Sum(hash[:0])
return hash
}
func (node *Node) Call(result interface{}, method string, args ...interface{}) { func (node *Node) Call(result interface{}, method string, args ...interface{}) {
Check((*rpc.Client)(node).Call(result, method, args...)) Check((*rpc.Client)(node).Call(result, method, args...))
...@@ -98,19 +71,10 @@ func (node *Node) GetBlockByNumber(blockNumber int64) types.Header { ...@@ -98,19 +71,10 @@ func (node *Node) GetBlockByNumber(blockNumber int64) types.Header {
return resp return resp
} }
func getSigner(header *types.Header) (signer string, err error) {
signature := header.Extra[len(header.Extra)-65:]
hash := sigHash(header).Bytes()
pubkey, err := crypto.Ecrecover(hash, signature)
address := make([]byte, 20)
copy(address, crypto.Keccak256(pubkey[1:])[12:])
signer = ToHex(address)
return
}
func (node *Node) GetBlockSigner(blockNumber int64) (signer string) { func (node *Node) GetBlockSigner(blockNumber int64) (signer string) {
header := node.GetBlockByNumber(blockNumber) header := node.GetBlockByNumber(blockNumber)
signer, err := getSigner(&header) signer, err := GetSigner(&header)
Check(err) Check(err)
return return
} }
...@@ -128,7 +92,10 @@ func (node *Node) GetSnapshotAtHash(hash common.Hash) (snapshot Snapshot) { ...@@ -128,7 +92,10 @@ func (node *Node) GetSnapshotAtHash(hash common.Hash) (snapshot Snapshot) {
} }
func (node *Node) GetSnapshotAtBlock(blockNumber int64) (snapshot Snapshot) { func (node *Node) GetSnapshotAtBlock(blockNumber int64) (snapshot Snapshot) {
node.Call(&snapshot, "clique_getSnapshot", fmt.Sprintf("0x%x", blockNumber)) if blockNumber < 0 {
return node.GetSnapshot()
}
node.Call(&snapshot, "clique_getSnapshot", Int64ToHex(blockNumber))
return return
} }
...@@ -136,7 +103,7 @@ func (node *Node) GetSigners() (signers []string) { ...@@ -136,7 +103,7 @@ func (node *Node) GetSigners() (signers []string) {
var s []common.Address var s []common.Address
node.Call(&s, "clique_getSigners", nil) node.Call(&s, "clique_getSigners", nil)
for _, signer := range s { for _, signer := range s {
signers = append(signers, ToHex(signer.Bytes())) signers = append(signers, BytesToHex(signer.Bytes()))
} }
return return
} }
...@@ -145,20 +112,28 @@ func (node *Node) GetSignersAtHash(hash common.Hash) (signers []string) { ...@@ -145,20 +112,28 @@ func (node *Node) GetSignersAtHash(hash common.Hash) (signers []string) {
var s []common.Address var s []common.Address
node.Call(&signers, "clique_getSignersAtHash", hash) node.Call(&signers, "clique_getSignersAtHash", hash)
for _, signer := range s { for _, signer := range s {
signers = append(signers, ToHex(signer.Bytes())) signers = append(signers, BytesToHex(signer.Bytes()))
} }
return return
} }
func (node *Node) GetSignersAtBlock(blockNumber int64) (signers []string) { func (node *Node) GetSignersAtBlock(blockNumber int64) (signers []string) {
var s []common.Address var s []common.Address
node.Call(&s, "clique_getSigners", "0x"+strconv.FormatInt(blockNumber, 16)) if blockNumber < 0 {
return node.GetSigners()
}
node.Call(&s, "clique_getSigners", Int64ToHex(blockNumber))
for _, signer := range s { for _, signer := range s {
signers = append(signers, ToHex(signer.Bytes())) signers = append(signers, BytesToHex(signer.Bytes()))
} }
return return
} }
func (node *Node) Propose(address string, vote bool){
node.Call(nil, "clique_propose", address, vote)
return
}
func (node *Node) GetVotes(blockNumber int64) (votes Proposals) { func (node *Node) GetVotes(blockNumber int64) (votes Proposals) {
var ( var (
snapshot Snapshot snapshot Snapshot
...@@ -170,17 +145,17 @@ func (node *Node) GetVotes(blockNumber int64) (votes Proposals) { ...@@ -170,17 +145,17 @@ func (node *Node) GetVotes(blockNumber int64) (votes Proposals) {
} }
votes.BlockNumber = int64(snapshot.Number) votes.BlockNumber = int64(snapshot.Number)
for signer := range snapshot.Signers { for signer := range snapshot.Signers {
votes.Signers = append(votes.Signers, ToHex(signer[:])) votes.Signers = append(votes.Signers, BytesToHex(signer[:]))
sort.Strings(votes.Signers) sort.Strings(votes.Signers)
} }
for proposal := range snapshot.Tally { for proposal := range snapshot.Tally {
votes.Proposals = append(votes.Proposals, ToHex(proposal[:])) votes.Proposals = append(votes.Proposals, BytesToHex(proposal[:]))
} }
votes.Votes = make(map[string]map[string]*bool) votes.Votes = make(map[string]map[string]*bool)
votes.Tally = make(map[string]*Tally) votes.Tally = make(map[string]*Tally)
for _, v := range snapshot.Votes { for _, v := range snapshot.Votes {
proposal := ToHex(v.Address[:]) proposal := BytesToHex(v.Address[:])
signer := ToHex(v.Signer[:]) signer := BytesToHex(v.Signer[:])
if votes.Votes[proposal] == nil { if votes.Votes[proposal] == nil {
votes.Votes[proposal] = make(map[string]*bool) votes.Votes[proposal] = make(map[string]*bool)
for _, signer := range votes.Signers { for _, signer := range votes.Signers {
...@@ -199,17 +174,20 @@ func (node *Node) GetVotes(blockNumber int64) (votes Proposals) { ...@@ -199,17 +174,20 @@ func (node *Node) GetVotes(blockNumber int64) (votes Proposals) {
return return
} }
func (node *Node) SealersStatus() map[string]int64 { func (node *Node) SealersStatus(blockNumber int64) (status map[string]int64) {
status := make(map[string]int64) if blockNumber == 0 { // Genesis block doesn't have signer
for _, address := range node.GetSigners() { return
}
status = make(map[string]int64)
for _, address := range node.GetSignersAtBlock(blockNumber) {
status[address] = -1 status[address] = -1
} }
notSeen := int64(len(status)) notSeen := int64(len(status))
block := node.GetBlockByNumber(-1) block := node.GetBlockByNumber(blockNumber)
blockNumber := block.Number.Int64() blockNumber = block.Number.Int64()
until := blockNumber - 5*notSeen until := Max(1, blockNumber - 5*notSeen)
for notSeen > 0 { for notSeen > 0 {
signer, _ := getSigner(&block) signer, _ := GetSigner(&block)
if status[signer] == -1 { if status[signer] == -1 {
status[signer] = block.Number.Int64() status[signer] = block.Number.Int64()
notSeen-- notSeen--
...@@ -275,7 +253,7 @@ func (node *Node) getSignerFirstBlock(signer string, since int64, until int64) ( ...@@ -275,7 +253,7 @@ func (node *Node) getSignerFirstBlock(signer string, since int64, until int64) (
ch <- found ch <- found
} }
} }
ch <- (-1) ch <- -1
}() }()
} }
for { for {
...@@ -339,7 +317,7 @@ func (node *Node) getSignerLastBlock(signer string, since int64, until int64) (b ...@@ -339,7 +317,7 @@ func (node *Node) getSignerLastBlock(signer string, since int64, until int64) (b
ch <- found ch <- found
} }
} }
ch <- (-1) ch <- -1
}() }()
} }
for { for {
...@@ -372,8 +350,6 @@ func (node *Node) getSignerLastBlock(signer string, since int64, until int64) (b ...@@ -372,8 +350,6 @@ func (node *Node) getSignerLastBlock(signer string, since int64, until int64) (b
func (node *Node) searchSnapshotForward(blockNumber int64, signer string, count int64) (found int64, visited int64) { func (node *Node) searchSnapshotForward(blockNumber int64, signer string, count int64) (found int64, visited int64) {
//printDebug("In",fmt.Sprintf("(%v, %v, %v)", blockNumber, signer, count))
//defer printDebug("Out", "")
found = -1 found = -1
if blockNumber+count < 1 { if blockNumber+count < 1 {
return return
...@@ -384,7 +360,7 @@ func (node *Node) searchSnapshotForward(blockNumber int64, signer string, count ...@@ -384,7 +360,7 @@ func (node *Node) searchSnapshotForward(blockNumber int64, signer string, count
visited += recents visited += recents
count -= recents count -= recents
for b, s := range snapshot.Recents { for b, s := range snapshot.Recents {
if ToHex(s[:]) == signer { if BytesToHex(s[:]) == signer {
found = int64(b) found = int64(b)
} }
} }
...@@ -393,8 +369,6 @@ func (node *Node) searchSnapshotForward(blockNumber int64, signer string, count ...@@ -393,8 +369,6 @@ func (node *Node) searchSnapshotForward(blockNumber int64, signer string, count
} }
func (node *Node) searchSnapshotBackward(blockNumber int64, signer string, count int64) (found int64, visited int64) { func (node *Node) searchSnapshotBackward(blockNumber int64, signer string, count int64) (found int64, visited int64) {
//printDebug("In",fmt.Sprintf("(%v, %v, %v)", blockNumber, signer, count))
//defer printDebug("Out", "")
found = -1 found = -1
if blockNumber < 1 { // Genesis block has no signers if blockNumber < 1 { // Genesis block has no signers
return return
...@@ -407,7 +381,7 @@ func (node *Node) searchSnapshotBackward(blockNumber int64, signer string, count ...@@ -407,7 +381,7 @@ func (node *Node) searchSnapshotBackward(blockNumber int64, signer string, count
count = Min(blockNumber, int64(2*len(snapshot.Signers))) count = Min(blockNumber, int64(2*len(snapshot.Signers)))
} }
for b, s := range snapshot.Recents { for b, s := range snapshot.Recents {
if ToHex(s[:]) == signer { if BytesToHex(s[:]) == signer {
found = int64(b) found = int64(b)
return return
} }
...@@ -434,10 +408,9 @@ func (node *Node) SealerInfo(sealer string) (info SealerInfo, err error) { ...@@ -434,10 +408,9 @@ func (node *Node) SealerInfo(sealer string) (info SealerInfo, err error) {
func Dial(url string) *Node { func Dial(url string) (*Node, error) {
client, err := rpc.Dial(url) client, err := rpc.Dial(url)
Check(err) return (*Node)(client), err
return (*Node)(client)
} }
func (node *Node) Close() { func (node *Node) Close() {
......
package main package main
import ( import (
"encoding/json" "../bfa"
"../util"
"flag" "flag"
"fmt" "fmt"
"log" "log"
...@@ -10,22 +11,78 @@ import ( ...@@ -10,22 +11,78 @@ import (
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"../util"
"../bfa"
) )
const ( const (
DefaultURL = "http://localhost:8545" DefaultURL = "http://localhost:8545"
latest = -1
) )
type Node = bfa.Node type Node = bfa.Node
func printVotes(node *bfa.Node, useJson bool) {
votes := node.GetVotes(-1) var (
if useJson { url string
v, err := json.MarshalIndent(votes, "", " ") json bool
util.Check(err) help bool
fmt.Println(string(v)) 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
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]\n%v\n", os.Args[0], command, 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", -1, "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 return
} }
fmt.Printf("Bloque: %d\nPropuestas en curso: %d\n", votes.BlockNumber, len(votes.Proposals)) fmt.Printf("Bloque: %d\nPropuestas en curso: %d\n", votes.BlockNumber, len(votes.Proposals))
...@@ -46,81 +103,107 @@ func printVotes(node *bfa.Node, useJson bool) { ...@@ -46,81 +103,107 @@ func printVotes(node *bfa.Node, useJson bool) {
} }
} }
func printSealerInfo(node *Node, sealer string) { func sealers() {
info, err := node.SealerInfo(sealer) var (
util.Check(err) blockNumber int64
v, err := json.MarshalIndent(info, "", " ") status bool
)
description = "Presenta la lista de selladores. Opcionalmente indica el último bloque sellado por cada uno."
setFlags()
flags.Int64Var(&blockNumber, "block-number", -1, "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 -1 si un nodo no ha sellado en las últimas 5 rondas.")
parseFlags()
if blockNumber == 0{
panic("El bloque génesis no tiene firmantes")
}
url = updateURL(url)
node, err := bfa.Dial(url)
util.Check(err) util.Check(err)
fmt.Println(string(v)) 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]] > sealers[list[j]] })
length := util.Max(2, int64(len(strconv.FormatInt(sealers[list[0]], 10))))
for _, sealer := range list {
fmt.Printf("%v: %*d\n", sealer, length, sealers[sealer])
}
} 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 printSealers(node *Node, useJson bool) { func propose() {
sealers := node.SealersStatus() var (
if useJson { all bool
v, err := json.MarshalIndent(sealers, "", " ") vote bool
util.Check(err) proposals []string
fmt.Println(string(v)) )
return description = "Vota por una propuesta."
} setFlags()
var list []string flags.BoolVar(&all, "all", false, "Vota en todas las propuestas activas")
for sealer := range sealers { flags.BoolVar(&vote, "vote", true, "Sentido del voto (true: a favor, false: en contra)")
list = append(list, sealer) parseFlags()
url = updateURL(url)
node, err := bfa.Dial(url)
util.Check(err)
defer node.Close()
if all {
votes := node.GetVotes(latest)
for _, proposal := range votes.Proposals {
proposals = append(proposals, proposal)
}
} else {
for i := 0; i < flags.NArg(); i++ {
proposals = append(proposals, flags.Arg(i))
}
} }
sort.Slice(list, func(i, j int) bool { return sealers[list[i]] > sealers[list[j]] }) for _, proposal := range proposals {
length := len(strconv.FormatInt(sealers[list[0]], 10)) node.Propose(proposal, vote)
for _, sealer := range list { fmt.Printf("Voto por %v: %v\n", proposal, vote)
fmt.Printf("%v: %*d\n", sealer, length, sealers[sealer])
} }
} }
func main() { func main() {
var ( var (
url string commands = map[string]func(){
useJson bool "proposals": proposals,
help bool "sealers": sealers,
flags flag.FlagSet "vote": propose,
command = ""
commands = []string{"sealers", "proposals", "sealerInfo"}
desc = map[string]string{
"proposals": "Detalla el estado de una votación",
"sealerInfo": "Brinda datos sobre un sealer",
"sealers": "Brinda información sobre la última actividad de los selladores",
} }
validCommands []string
) )
for command := range commands {
validCommands = append(validCommands, command)
}
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
log.Fatalf("Error: %s", err) log.Printf("Error: %s", err)
usage(1)
} }
}() }()
if len(os.Args) > 1 { if len(os.Args) > 1 {
command = os.Args[1] command = os.Args[1]
} }
if !util.Contains(commands, command) { if commands[command] == nil {
fmt.Fprintf(os.Stderr, "Uso: %v <%v> [opciones]\n", path.Base(os.Args[0]), strings.Join(commands, "|")) fmt.Fprintf(os.Stderr, "Uso: %v <%v> [opciones]\n", path.Base(os.Args[0]), strings.Join(validCommands, "|"))
fmt.Fprintf(os.Stderr, "For help: %v <command> -h\n", path.Base(os.Args[0])) fmt.Fprintf(os.Stderr, "Para ayuda: %v <command> -h\n", path.Base(os.Args[0]))
os.Exit(1) os.Exit(1)
} }
flags.BoolVar(&useJson, "json", false, "Produce salida en formato json") commands[command]()
flags.StringVar(&url, "url", DefaultURL, "URL para conexión con geth. Ejemplo: /home/bfa/bfa/network/node/geth.ipc")
//flags.BoolVar(&help, "h", false, "")
//flags.BoolVar(&help, "help", false, "Muestra esta ayuda")
flags.Parse(os.Args[2:])
if help {
fmt.Fprintf(os.Stderr, "Uso: %v %v [opciones]\n%v\nOpciones: \n", path.Base(os.Args[0]), command, desc[command])
flags.PrintDefaults()
return
}
node := bfa.Dial(url)
defer node.Close()
switch command {
case "sealers":
printSealers(node, useJson)
case "proposals":
printVotes(node, useJson)
case "sealerInfo":
for i := 0; i < flags.NArg(); i++ {
printSealerInfo(node, flags.Arg(i))
}
}
} }
...@@ -39,6 +39,6 @@ func GetSigner(header *types.Header) (signer string, err error) { ...@@ -39,6 +39,6 @@ func GetSigner(header *types.Header) (signer string, err error) {
pubkey, err := crypto.Ecrecover(hash, signature) pubkey, err := crypto.Ecrecover(hash, signature)
address := make([]byte, 20) address := make([]byte, 20)
copy(address, crypto.Keccak256(pubkey[1:])[12:]) copy(address, crypto.Keccak256(pubkey[1:])[12:])
signer = util.ToHex(address) signer = util.BytesToHex(address)
return return
} }
package util package util
import ( import (
"encoding/json"
"fmt" "fmt"
"runtime" "runtime"
"strconv"
) )
func Contains(slice []string, s string) bool { func Contains(slice []string, s string) bool {
...@@ -20,10 +22,14 @@ func Check(err error) { ...@@ -20,10 +22,14 @@ func Check(err error) {
} }
} }
func ToHex(b []byte) string { func BytesToHex(b []byte) string {
return fmt.Sprintf("0x%02x", b) return fmt.Sprintf("0x%02x", b)
} }
func Int64ToHex(n int64) string {
return "0x"+strconv.FormatInt(n,16)
}
func PrintDebug(prefix, suffix string) { func PrintDebug(prefix, suffix string) {
ptr, _, _, _ := runtime.Caller(1) ptr, _, _, _ := runtime.Caller(1)
fmt.Printf("%v: %v%v\n", prefix, runtime.FuncForPC(ptr).Name(), suffix) fmt.Printf("%v: %v%v\n", prefix, runtime.FuncForPC(ptr).Name(), suffix)
...@@ -36,6 +42,19 @@ func Min(a, b int64) int64 { ...@@ -36,6 +42,19 @@ func Min(a, b int64) int64 {
return b 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
}
......
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