From 9b0d2d5b2189a6e02e6a276a30212f5dd80f0d4f Mon Sep 17 00:00:00 2001 From: Miguel Montes <miguel.montes@gmail.com> Date: Tue, 13 Nov 2018 15:22:11 -0300 Subject: [PATCH] =?UTF-8?q?Agregado=20c=C3=B3digo=20de=20votaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bfa_client/Makefile | 4 +- bfa_client/src/bfa/node.go | 103 +++++-------- bfa_client/src/client/bfa_client.go | 215 +++++++++++++++++++--------- bfa_client/src/clique/clique.go | 2 +- bfa_client/src/util/util.go | 21 ++- 5 files changed, 211 insertions(+), 134 deletions(-) diff --git a/bfa_client/Makefile b/bfa_client/Makefile index 918125f..77f6e40 100644 --- a/bfa_client/Makefile +++ b/bfa_client/Makefile @@ -1,6 +1,8 @@ +SRC:=src/client/bfa_client.go src/bfa/node.go src/util/util.go src/clique/clique.go + all: bin/bfa_client -bin/bfa_client: src/client/bfa_client.go | bin +bin/bfa_client: $(SRC) | bin go build -o $@ $< bin: diff --git a/bfa_client/src/bfa/node.go b/bfa_client/src/bfa/node.go index 73f9b86..aac88a5 100644 --- a/bfa_client/src/bfa/node.go +++ b/bfa_client/src/bfa/node.go @@ -1,17 +1,14 @@ package bfa import ( + . "../clique" . "../util" "fmt" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/clique" "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" "sort" - "strconv" ) type Node rpc.Client @@ -47,30 +44,6 @@ type SealerInfo struct { 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{}) { Check((*rpc.Client)(node).Call(result, method, args...)) @@ -98,19 +71,10 @@ func (node *Node) GetBlockByNumber(blockNumber int64) types.Header { 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) { header := node.GetBlockByNumber(blockNumber) - signer, err := getSigner(&header) + signer, err := GetSigner(&header) Check(err) return } @@ -128,7 +92,10 @@ func (node *Node) GetSnapshotAtHash(hash common.Hash) (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 } @@ -136,7 +103,7 @@ func (node *Node) GetSigners() (signers []string) { var s []common.Address node.Call(&s, "clique_getSigners", nil) for _, signer := range s { - signers = append(signers, ToHex(signer.Bytes())) + signers = append(signers, BytesToHex(signer.Bytes())) } return } @@ -145,20 +112,28 @@ func (node *Node) GetSignersAtHash(hash common.Hash) (signers []string) { var s []common.Address node.Call(&signers, "clique_getSignersAtHash", hash) for _, signer := range s { - signers = append(signers, ToHex(signer.Bytes())) + signers = append(signers, BytesToHex(signer.Bytes())) } return } func (node *Node) GetSignersAtBlock(blockNumber int64) (signers []string) { 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 { - signers = append(signers, ToHex(signer.Bytes())) + signers = append(signers, BytesToHex(signer.Bytes())) } 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) { var ( snapshot Snapshot @@ -170,17 +145,17 @@ func (node *Node) GetVotes(blockNumber int64) (votes Proposals) { } votes.BlockNumber = int64(snapshot.Number) for signer := range snapshot.Signers { - votes.Signers = append(votes.Signers, ToHex(signer[:])) + votes.Signers = append(votes.Signers, BytesToHex(signer[:])) sort.Strings(votes.Signers) } 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.Tally = make(map[string]*Tally) for _, v := range snapshot.Votes { - proposal := ToHex(v.Address[:]) - signer := ToHex(v.Signer[:]) + proposal := BytesToHex(v.Address[:]) + signer := BytesToHex(v.Signer[:]) if votes.Votes[proposal] == nil { votes.Votes[proposal] = make(map[string]*bool) for _, signer := range votes.Signers { @@ -199,17 +174,20 @@ func (node *Node) GetVotes(blockNumber int64) (votes Proposals) { return } -func (node *Node) SealersStatus() map[string]int64 { - status := make(map[string]int64) - for _, address := range node.GetSigners() { +func (node *Node) SealersStatus(blockNumber int64) (status map[string]int64) { + if blockNumber == 0 { // Genesis block doesn't have signer + return + } + status = make(map[string]int64) + for _, address := range node.GetSignersAtBlock(blockNumber) { status[address] = -1 } notSeen := int64(len(status)) - block := node.GetBlockByNumber(-1) - blockNumber := block.Number.Int64() - until := blockNumber - 5*notSeen + block := node.GetBlockByNumber(blockNumber) + blockNumber = block.Number.Int64() + until := Max(1, blockNumber - 5*notSeen) for notSeen > 0 { - signer, _ := getSigner(&block) + signer, _ := GetSigner(&block) if status[signer] == -1 { status[signer] = block.Number.Int64() notSeen-- @@ -275,7 +253,7 @@ func (node *Node) getSignerFirstBlock(signer string, since int64, until int64) ( ch <- found } } - ch <- (-1) + ch <- -1 }() } for { @@ -339,7 +317,7 @@ func (node *Node) getSignerLastBlock(signer string, since int64, until int64) (b ch <- found } } - ch <- (-1) + ch <- -1 }() } for { @@ -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) { - //printDebug("In",fmt.Sprintf("(%v, %v, %v)", blockNumber, signer, count)) - //defer printDebug("Out", "") found = -1 if blockNumber+count < 1 { return @@ -384,7 +360,7 @@ func (node *Node) searchSnapshotForward(blockNumber int64, signer string, count visited += recents count -= recents for b, s := range snapshot.Recents { - if ToHex(s[:]) == signer { + if BytesToHex(s[:]) == signer { found = int64(b) } } @@ -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) { - //printDebug("In",fmt.Sprintf("(%v, %v, %v)", blockNumber, signer, count)) - //defer printDebug("Out", "") found = -1 if blockNumber < 1 { // Genesis block has no signers return @@ -407,7 +381,7 @@ func (node *Node) searchSnapshotBackward(blockNumber int64, signer string, count count = Min(blockNumber, int64(2*len(snapshot.Signers))) } for b, s := range snapshot.Recents { - if ToHex(s[:]) == signer { + if BytesToHex(s[:]) == signer { found = int64(b) return } @@ -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) - Check(err) - return (*Node)(client) + return (*Node)(client), err } func (node *Node) Close() { diff --git a/bfa_client/src/client/bfa_client.go b/bfa_client/src/client/bfa_client.go index a3ee59e..d308f45 100644 --- a/bfa_client/src/client/bfa_client.go +++ b/bfa_client/src/client/bfa_client.go @@ -1,7 +1,8 @@ package main import ( - "encoding/json" + "../bfa" + "../util" "flag" "fmt" "log" @@ -10,22 +11,78 @@ import ( "sort" "strconv" "strings" - "../util" - "../bfa" ) const ( DefaultURL = "http://localhost:8545" + latest = -1 ) type Node = bfa.Node -func printVotes(node *bfa.Node, useJson bool) { - votes := node.GetVotes(-1) - if useJson { - v, err := json.MarshalIndent(votes, "", " ") - util.Check(err) - fmt.Println(string(v)) + +var ( + url string + json bool + help bool + flags flag.FlagSet + command string + description string +) + +func setFlags() { + flags.BoolVar(&json, "json", false, "Produce salida en formato json") + flags.StringVar(&url, "url", "", "URL para conexión con geth. Ejemplo: '/home/bfa/bfa/network/node/geth.ipc' o 'http://localhost:8545'") + flags.BoolVar(&help, "h", false, "") + flags.BoolVar(&help, "help", false, "Muestra esta ayuda") +} + +func updateURL(url string) (updated string) { + const ( + defaultIPC = "/home/bfa/bfa/network/node/geth.ipc" + defaultHTTP = "http://localhost:8545" + ) + if url != "" { // We accept the user selected URL + 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 } 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) { } } -func printSealerInfo(node *Node, sealer string) { - info, err := node.SealerInfo(sealer) - util.Check(err) - v, err := json.MarshalIndent(info, "", " ") +func sealers() { + var ( + blockNumber int64 + 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) - 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) { - sealers := node.SealersStatus() - if useJson { - v, err := json.MarshalIndent(sealers, "", " ") - util.Check(err) - fmt.Println(string(v)) - return - } - var list []string - for sealer := range sealers { - list = append(list, sealer) +func propose() { + var ( + all bool + vote bool + proposals []string + ) + description = "Vota por una propuesta." + setFlags() + flags.BoolVar(&all, "all", false, "Vota en todas las propuestas activas") + flags.BoolVar(&vote, "vote", 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() + 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]] }) - length := len(strconv.FormatInt(sealers[list[0]], 10)) - for _, sealer := range list { - fmt.Printf("%v: %*d\n", sealer, length, sealers[sealer]) + for _, proposal := range proposals { + node.Propose(proposal, vote) + fmt.Printf("Voto por %v: %v\n", proposal, vote) } } - func main() { var ( - url string - useJson bool - help bool - flags flag.FlagSet - 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", + 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.Fatalf("Error: %s", err) + log.Printf("Error: %s", err) + usage(1) } }() if len(os.Args) > 1 { command = os.Args[1] } - if !util.Contains(commands, command) { - fmt.Fprintf(os.Stderr, "Uso: %v <%v> [opciones]\n", path.Base(os.Args[0]), strings.Join(commands, "|")) - fmt.Fprintf(os.Stderr, "For help: %v <command> -h\n", path.Base(os.Args[0])) + 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) } - flags.BoolVar(&useJson, "json", false, "Produce salida en formato json") - 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)) - } - } + commands[command]() } diff --git a/bfa_client/src/clique/clique.go b/bfa_client/src/clique/clique.go index f4aa9c2..87871e4 100644 --- a/bfa_client/src/clique/clique.go +++ b/bfa_client/src/clique/clique.go @@ -39,6 +39,6 @@ func GetSigner(header *types.Header) (signer string, err error) { pubkey, err := crypto.Ecrecover(hash, signature) address := make([]byte, 20) copy(address, crypto.Keccak256(pubkey[1:])[12:]) - signer = util.ToHex(address) + signer = util.BytesToHex(address) return } diff --git a/bfa_client/src/util/util.go b/bfa_client/src/util/util.go index 83c4197..610bcca 100644 --- a/bfa_client/src/util/util.go +++ b/bfa_client/src/util/util.go @@ -1,8 +1,10 @@ package util import ( + "encoding/json" "fmt" "runtime" + "strconv" ) func Contains(slice []string, s string) bool { @@ -20,10 +22,14 @@ func Check(err error) { } } -func ToHex(b []byte) string { +func BytesToHex(b []byte) string { return fmt.Sprintf("0x%02x", b) } +func Int64ToHex(n int64) string { + return "0x"+strconv.FormatInt(n,16) +} + func PrintDebug(prefix, suffix string) { ptr, _, _, _ := runtime.Caller(1) fmt.Printf("%v: %v%v\n", prefix, runtime.FuncForPC(ptr).Name(), suffix) @@ -36,6 +42,19 @@ func Min(a, b int64) int64 { 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 +} -- GitLab