diff --git a/bfa_client/Makefile b/bfa_client/Makefile
index 918125f697b6a97b38ca0b25c8d0481ded9a6b95..77f6e402f22cf55142936801e2a3c10114eb9838 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 73f9b867e41b15e7170e132b9f3d8db88af5bd38..aac88a524d3bc723b3b2347c876f42985816295f 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 a3ee59eafda9eea147f8e702a13d6e1ef535d62c..d308f45342826ff04ec276d522fa403e791bda01 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 f4aa9c23f9b2efc0539bfcee9ff26c1652859bff..87871e496b26e5a1fbdeef389d0e7c3f7ab9b20f 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 83c419759917af62c6850fb7930b6ef4ab1165c8..610bcca8a48ca629c54e7486a0b785c960126f02 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
+}