diff --git a/bfa_client/src/bfa/node.go b/bfa_client/src/bfa/node.go index aac88a524d3bc723b3b2347c876f42985816295f..349d9d1f68365605392e955142657d7983feb4eb 100644 --- a/bfa_client/src/bfa/node.go +++ b/bfa_client/src/bfa/node.go @@ -44,6 +44,10 @@ type SealerInfo struct { LastBlock int64 } +const ( + Latest = "latest" + Self = "self" +) func (node *Node) Call(result interface{}, method string, args ...interface{}) { Check((*rpc.Client)(node).Call(result, method, args...)) @@ -55,7 +59,21 @@ func (node *Node) blockNumber() int64 { return bn.Int64() } - +func (node *Node) Coinbase() (coinbase string, err error) { + defer func(){ + if e := recover(); e != nil { + switch s := e.(type){ + case string: err = fmt.Errorf(s) + case error: err = s + default: err = fmt.Errorf("unknown error while getting coinbase: %v", e) + } + } + }() + var address common.Address + node.Call(&address, "eth_coinbase") + coinbase = BytesToHex(address[:]) + return +} func (node *Node) GetBlockByNumber(blockNumber int64) types.Header { var ( @@ -63,7 +81,7 @@ func (node *Node) GetBlockByNumber(blockNumber int64) types.Header { resp types.Header ) if blockNumber < 0 { - number = "latest" + number = Latest } else { number = fmt.Sprintf("0x%x", blockNumber) } @@ -71,7 +89,6 @@ func (node *Node) GetBlockByNumber(blockNumber int64) types.Header { return resp } - func (node *Node) GetBlockSigner(blockNumber int64) (signer string) { header := node.GetBlockByNumber(blockNumber) signer, err := GetSigner(&header) @@ -79,8 +96,6 @@ func (node *Node) GetBlockSigner(blockNumber int64) (signer string) { return } - - func (node *Node) GetSnapshot() (snapshot Snapshot) { node.Call(&snapshot, "clique_getSnapshot", nil) return @@ -129,7 +144,17 @@ func (node *Node) GetSignersAtBlock(blockNumber int64) (signers []string) { return } -func (node *Node) Propose(address string, vote bool){ +func (node *Node) IsSealer(address string) bool { + if address == Self { + var err error + if address, err = node.Coinbase(); err != nil { + return false + } + } + return Contains(node.GetSigners(), address) +} + +func (node *Node) Propose(address string, vote bool) { node.Call(nil, "clique_propose", address, vote) return } @@ -185,7 +210,7 @@ func (node *Node) SealersStatus(blockNumber int64) (status map[string]int64) { notSeen := int64(len(status)) block := node.GetBlockByNumber(blockNumber) blockNumber = block.Number.Int64() - until := Max(1, blockNumber - 5*notSeen) + until := Max(1, blockNumber-5*notSeen) for notSeen > 0 { signer, _ := GetSigner(&block) if status[signer] == -1 { @@ -201,7 +226,6 @@ func (node *Node) SealersStatus(blockNumber int64) (status map[string]int64) { return status } - func (node *Node) GetSealerInception(address string) (since int64) { if signers := node.GetSigners(); !Contains(signers, address) { return -1 @@ -226,7 +250,8 @@ func (node *Node) getSignerFirstBlock(signer string, since int64, until int64) ( return -1 } //snapshot := node.GetSnapshotAtBlock(since) - var (count int64 = 20 + var ( + count int64 = 20 found int64 = -1 ) // first, we look close to the inception @@ -348,7 +373,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) { found = -1 if blockNumber+count < 1 { @@ -392,8 +416,6 @@ func (node *Node) searchSnapshotBackward(blockNumber int64, signer string, count } } - - func (node *Node) SealerInfo(sealer string) (info SealerInfo, err error) { info.Address = sealer info.CurrentBlock = node.blockNumber() @@ -406,8 +428,6 @@ func (node *Node) SealerInfo(sealer string) (info SealerInfo, err error) { return } - - func Dial(url string) (*Node, error) { client, err := rpc.Dial(url) return (*Node)(client), err @@ -416,5 +436,3 @@ func Dial(url string) (*Node, error) { func (node *Node) Close() { (*rpc.Client)(node).Close() } - - diff --git a/bfa_client/src/client/bfa_client.go b/bfa_client/src/client/bfa_client.go index d308f45342826ff04ec276d522fa403e791bda01..4121a065fb634280fce51a390f171e2a7def532d 100644 --- a/bfa_client/src/client/bfa_client.go +++ b/bfa_client/src/client/bfa_client.go @@ -28,6 +28,7 @@ var ( flags flag.FlagSet command string description string + otherArgs string ) func setFlags() { @@ -58,7 +59,7 @@ func updateURL(url string) (updated string) { } func usage(errorCode int) { - fmt.Fprintf(os.Stderr, "Uso: %v %v [opciones]\n%v\n", os.Args[0], command, description) + fmt.Fprintf(os.Stderr, "Uso: %v %v [opciones] %v\n%v\n", os.Args[0], command, otherArgs, description) flags.PrintDefaults() os.Exit(errorCode) } @@ -151,31 +152,48 @@ func sealers() { func propose() { var ( all bool - vote bool + 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(&vote, "vote", true, "Sentido del voto (true: a favor, false: en contra)") + 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, vote) - fmt.Printf("Voto por %v: %v\n", proposal, vote) + node.Propose(proposal, authorize) + fmt.Printf("Voto por %v: %v\n", proposal, authorize) } } diff --git a/bfa_client/src/util/util.go b/bfa_client/src/util/util.go index 610bcca8a48ca629c54e7486a0b785c960126f02..0e651cca4d1653458e9e24e61e364ca9870c241f 100644 --- a/bfa_client/src/util/util.go +++ b/bfa_client/src/util/util.go @@ -1,8 +1,10 @@ package util import ( + "encoding/hex" "encoding/json" "fmt" + "github.com/ethereum/go-ethereum/common" "runtime" "strconv" ) @@ -57,5 +59,16 @@ func PrintJson(s interface{}){ } +func IsAddress(address string) bool { + bytes, err := hex.DecodeString(address[2:]) + return err == nil && len(bytes) == common.AddressLength +} + +func PanicIf(cond bool, format string, args ...interface{}){ + if cond { + panic(fmt.Sprintf(format, args...)) + } +} +