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

Commit inicial de bfa_client.go

parent ed46b02e
No related branches found
No related tags found
No related merge requests found
package main
import (
"encoding/json"
"flag"
"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"
"log"
"os"
"path"
"sort"
"strconv"
"strings"
)
const (
DefaultURL = "http://localhost:8545"
)
type Node rpc.Client
func toHex(b []byte) string {
return fmt.Sprintf("0x%02x", b)
}
func (node *Node) blockNumber() int64 {
var bn rpc.BlockNumber
node.Call(&bn, "eth_blockNumber")
return bn.Int64()
}
func check(err error) {
if err != nil {
panic(err.Error())
}
}
func (node *Node) Call(result interface{}, method string, args ...interface{}) {
check((*rpc.Client)(node).Call(result, method, args...))
}
func (node *Node) getBlockByNumber(blockNumber int64) types.Header {
var (
number string
resp types.Header
)
if blockNumber < 0 {
number = "latest"
} else {
number = fmt.Sprintf("0x%x", blockNumber)
}
node.Call(&resp, "eth_getBlockByNumber", number, false)
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) {
fmt.Printf("in getBlockSigner(%d)\n",blockNumber)
defer fmt.Printf("out getBlockSigner(%d)\n",blockNumber)
header := node.getBlockByNumber(blockNumber)
signer, err := getSigner(&header)
check(err)
return
}
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[common.Address]struct{} `json:"signers"` // Set of authorized signers at this moment
Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections
Votes []*clique.Vote `json:"votes"` // List of votes cast in chronological order
Tally map[common.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 []string `json:"proposals"` // List of proposals being voted
Signers []string `json:"signers"` // List of authorized signers at this moment
Tally map[string]*Tally `json:"tally"` // Count of positive, negative and empty votes for a proposal
Votes map[string]map[string]*bool `json:"votes"` // List of votes for each proposal
}
func (node *Node) getSnapshot() (snapshot Snapshot) {
node.Call(&snapshot, "clique_getSnapshot", nil)
return
}
func (node *Node) getSnapshotAtHash(hash common.Hash) (snapshot Snapshot) {
node.Call(&snapshot, "clique_getSnapshotAtHash", hash)
return
}
func (node *Node) getSnapshotAtBlock(blockNumber int64) Snapshot {
block := node.getBlockByNumber(blockNumber)
return node.getSnapshotAtHash(block.Hash())
}
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()))
}
return
}
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()))
}
return
}
func (node *Node) getSignersAtBlock(blockNumber int64) []string {
block := node.getBlockByNumber(blockNumber)
return node.getSignersAtHash(block.Hash())
}
func (node *Node) getVotes(blockNumber int64) (votes Proposals) {
var (
snapshot Snapshot
)
if blockNumber < 0 {
snapshot = node.getSnapshot()
} else {
snapshot = node.getSnapshotAtBlock(blockNumber)
}
votes.BlockNumber = int64(snapshot.Number)
for signer := range snapshot.Signers {
votes.Signers = append(votes.Signers, toHex(signer[:]))
sort.Strings(votes.Signers)
}
for proposal := range snapshot.Tally {
votes.Proposals = append(votes.Proposals, toHex(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[:])
if votes.Votes[proposal] == nil {
votes.Votes[proposal] = make(map[string]*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) sealersStatus() map[string]int64 {
status := make(map[string]int64)
for _, address := range node.getSigners() {
status[address] = -1
}
notSeen := int64(len(status))
block := node.getBlockByNumber(-1)
blockNumber := block.Number.Int64()
until := blockNumber - 5*notSeen
for notSeen > 0 {
signer, _ := getSigner(&block)
if status[signer] == -1 {
status[signer] = block.Number.Int64()
notSeen--
}
if blockNumber == until {
break
}
blockNumber--
block = node.getBlockByNumber(blockNumber)
}
return status
}
func (node *Node) getSealerInception(address string) (since int64) {
fmt.Println("in getSealerInception")
defer fmt.Println("out getSealerInception")
if signers := node.getSigners(); !contains(signers, address){
return -1
}
lo := int64(0)
hi := node.blockNumber()
for lo < hi {
mid := lo + (hi - lo) / 2
signers := node.getSignersAtBlock(mid)
if contains(signers, address){
hi = mid
} else {
lo = mid + 1
}
}
return hi
}
func (node *Node) getSignerFirstBlock(signer string, since int64, until int64) (blockNumber int64){
fmt.Println("in getSignerFirstBlock")
defer fmt.Println("out getSignerFirstBlock")
if since < 0{
return -1
}
for i := since; i <= until; i++ {
if signer == node.getBlockSigner(i){
return i
}
}
return -1
}
func (node *Node) getSignerLastBlock(signer string, since int64, until int64) (blockNumber int64){
fmt.Println("in getSignerLastBlock")
defer fmt.Println("out getSignerLastBlock")
if since < 0 {
return -1
}
for i := until; i >= since; i-- {
if signer == node.getBlockSigner(i){
return i
}
}
return -1
}
type SealerInfo struct {
Address string
CurrentBlock int64
Since int64
FirstBlock int64
LastBlock int64
}
func (node *Node) sealerInfo(sealer string) (info SealerInfo, err error) {
fmt.Println("in sealerInfo")
defer fmt.Println("out sealerInfo")
info.Address = sealer
info.CurrentBlock = node.blockNumber()
info.Since = node.getSealerInception(sealer)
if info.Since == -1 {
return info, fmt.Errorf("%q is not a sealer", sealer)
}
info.FirstBlock = node.getSignerFirstBlock(sealer, info.Since+1, info.CurrentBlock )
info.LastBlock = node.getSignerLastBlock(sealer,info.FirstBlock, info.CurrentBlock)
return
}
func printVotes(node *Node, useJson bool) {
votes := node.getVotes(-1)
if useJson {
v, err := json.MarshalIndent(votes, "", " ")
check(err)
fmt.Println(string(v))
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 printSealerInfo(node *Node, sealer string){
fmt.Println("in printSealerInfo")
defer fmt.Println("out printSealerInfo")
info, err := node.sealerInfo(sealer)
check(err)
v, err := json.MarshalIndent(info, "", " ")
check(err)
fmt.Println(string(v))
}
func printSealers(node *Node, useJson bool) {
sealers := node.sealersStatus()
if useJson {
v, err := json.MarshalIndent(sealers, "", " ")
check(err)
fmt.Println(string(v))
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 := len(strconv.FormatInt(sealers[list[0]], 10))
for _, sealer := range list {
fmt.Printf("%v: %*d\n", sealer, length, sealers[sealer])
}
}
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 Dial(url string) *Node {
client, err := rpc.Dial(url)
check(err)
return (*Node)(client)
}
func (node *Node) Close() {
(*rpc.Client)(node).Close()
}
func contains(slice []string, s string) bool {
for _, x := range slice {
if x == s {
return true
}
}
return false
}
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",
}
)
defer func() {
if err := recover(); err != nil {
log.Fatalf("Error: %s", err)
}
}()
if len(os.Args) > 1 {
command = os.Args[1]
}
if !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]))
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 := Dial(url)
defer node.Close()
switch command {
case "sealers":
printSealers(node, useJson)
case "proposals":
printVotes(node, useJson)
case "sealerInfo":
fmt.Println(flags.NArg(), flags.Args())
for i := 0; i < flags.NArg(); i++ {
fmt.Println(flags.Arg(i))
printSealerInfo(node,flags.Arg(i))
}
}
}
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