diff --git a/vote_status/README.md b/vote_status/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c4bf6bc87ba2335245f019d03c745c91cfd5a8f1 --- /dev/null +++ b/vote_status/README.md @@ -0,0 +1,72 @@ +# `vote_status.py` + +Script en Python que reporta el estado de una votación. + +Este es un _script_ escrito en Python, que requiere Python 3 y la biblioteca web3 (que puede instalarse con `pip`). + +Para funcionar requiere conocer el directorio donde está el _socket_ `geth.ipc` (asume que es `${BFANETWORKDIR}/node/geth.ipc` o, en su defecto `~/bfa/network/node/geth.ipc`), y el puerto RPC (asume que es el 8545). Ambos valores pueden especificarse en la lÃnea de comandos. + +El control de errores es nulo. Cualquier error de conexÃón con `geth` producirá un _stacktrace_. + +``` +bfa@bootnode:~$ vote_status.py +Propuesta: 0x2388d2cdb2cd6e7722b4af39c3bb406dd31f560e + 0x19fe7b9b3a1bebde77c5374c8e13c623e3d1b5b2: + 0x2feb6a8876bd9e2116b47834b977506a08ea77bd: True + 0x342e1d075d820ed3f9d9a05967ec4055ab23fa1e: + 0x39170a1ce03729d141dfaf8077c08b72c9cfdd0c: + 0x46991ada2a2544468eb3673524641bf293f23ccc: True + 0x609043ebde4a06bd28a1de238848e8f82cca9c23: True + 0x91c055c6478bd0ad6d19bcb58f5e7ca7b04e67f1: + 0x998c2651db6f76ca568c0071667d265bcc1b1e98: + 0x9b3ac6719b02ec7bb4820ae178d31c0bbda3a4e0: True + 0xc0310a7b3b25f49b11b901a667208a3eda8d7ceb: +A favor: 4, en contra: 0, no votaron: 6 + +Propuesta: 0x401d7a8432caa1025d5f093276cc6ec957b87c00 + 0x19fe7b9b3a1bebde77c5374c8e13c623e3d1b5b2: + 0x2feb6a8876bd9e2116b47834b977506a08ea77bd: True + 0x342e1d075d820ed3f9d9a05967ec4055ab23fa1e: + 0x39170a1ce03729d141dfaf8077c08b72c9cfdd0c: + 0x46991ada2a2544468eb3673524641bf293f23ccc: True + 0x609043ebde4a06bd28a1de238848e8f82cca9c23: True + 0x91c055c6478bd0ad6d19bcb58f5e7ca7b04e67f1: + 0x998c2651db6f76ca568c0071667d265bcc1b1e98: + 0x9b3ac6719b02ec7bb4820ae178d31c0bbda3a4e0: True + 0xc0310a7b3b25f49b11b901a667208a3eda8d7ceb: +A favor: 4, en contra: 0, no votaron: 6 + +bfa@bootnode:~$ +``` + +Usado con `watch` permite un seguimiento en terminal un poco más compacto que `walker.pl`. + +``` +Every 5.0s: vote_status.py + +Propuesta: 0x2388d2cdb2cd6e7722b4af39c3bb406dd31f560e + 0x19fe7b9b3a1bebde77c5374c8e13c623e3d1b5b2: + 0x2feb6a8876bd9e2116b47834b977506a08ea77bd: True + 0x342e1d075d820ed3f9d9a05967ec4055ab23fa1e: + 0x39170a1ce03729d141dfaf8077c08b72c9cfdd0c: + 0x46991ada2a2544468eb3673524641bf293f23ccc: True + 0x609043ebde4a06bd28a1de238848e8f82cca9c23: True + 0x91c055c6478bd0ad6d19bcb58f5e7ca7b04e67f1: + 0x998c2651db6f76ca568c0071667d265bcc1b1e98: + 0x9b3ac6719b02ec7bb4820ae178d31c0bbda3a4e0: True + 0xc0310a7b3b25f49b11b901a667208a3eda8d7ceb: +A favor: 4, en contra: 0, no votaron: 6 + +Propuesta: 0x401d7a8432caa1025d5f093276cc6ec957b87c00 + 0x19fe7b9b3a1bebde77c5374c8e13c623e3d1b5b2: + 0x2feb6a8876bd9e2116b47834b977506a08ea77bd: True + 0x342e1d075d820ed3f9d9a05967ec4055ab23fa1e: + 0x39170a1ce03729d141dfaf8077c08b72c9cfdd0c: + 0x46991ada2a2544468eb3673524641bf293f23ccc: True + 0x609043ebde4a06bd28a1de238848e8f82cca9c23: True + 0x91c055c6478bd0ad6d19bcb58f5e7ca7b04e67f1: + 0x998c2651db6f76ca568c0071667d265bcc1b1e98: + 0x9b3ac6719b02ec7bb4820ae178d31c0bbda3a4e0: True + 0xc0310a7b3b25f49b11b901a667208a3eda8d7ceb: +A favor: 4, en contra: 0, no votaron: 6 +``` diff --git a/vote_status/vote_status.py b/vote_status/vote_status.py new file mode 100755 index 0000000000000000000000000000000000000000..d426122922bac9630b83b0efc4307e6080c8ae55 --- /dev/null +++ b/vote_status/vote_status.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +from os import environ +ipc_path = "{}/node/geth.ipc".format(environ.get('BFANETWORKDIR', "~/bfa/network")) +rpc_port = 8545 +rpc_host = "localhost" +import requests +from web3 import Web3, IPCProvider +from web3.middleware import geth_poa_middleware +from itertools import groupby +import argparse + + +def getSigners(): + session = requests.Session() + payload = {"jsonrpc":"2.0", "method":'clique_getSigners',"params":[],"id":1} + headers = {'Content-type':'application/json'} + response = session.post( + "http://{}:{}".format(rpc_host,rpc_port), + json=payload, + headers=headers) + return response.json()['result'] + +def getVotes(): + session = requests.Session() + payload = {"jsonrpc":"2.0", "method":'clique_getSnapshot',"params":[],"id":1} + headers = {'Content-type':'application/json'} + response = session.post( + "http://{}:{}".format(rpc_host,rpc_port), + json=payload, + headers=headers) + votes = {} + for vote in response.json()['result']['votes']: + if vote['address'] not in votes: + votes[vote['address']] = {signer:"" for signer in getSigners()} + votes[vote['address']][vote['signer']] = vote['authorize'] + return votes + + + +if __name__ == "__main__": + + parser = argparse.ArgumentParser(description= + """Muestra el estado de una votación""") + parser.add_argument("--ipc-path", help ="Path del archivo geth.ipc. Default: {}".format(ipc_path)) + parser.add_argument("--rpc-port", help ="Puerto RPC. Default: {}".format(rpc_port),type=int) + args = parser.parse_args() + if args.ipc_path: + ipc_path = args.ipc_path + if args.rpc_port: + rpc_port = args.rpc_port + w3 = Web3(Web3.IPCProvider(ipc_path)) + w3.middleware_stack.inject(geth_poa_middleware, layer=0) + for proposal, votes in sorted(getVotes().items()): + print("Propuesta: {}".format(proposal)) + for voter, vote in sorted(votes.items()): + print("\t{}: {}".format(voter,vote)) + tally = { k:len(list(v)) for (k,v) in groupby(sorted(map(str,votes.values())))} + print("A favor: {}, en contra: {}, no votaron: {}\n".format( + tally.get("True",0), + tally.get("False",0), + tally.get("",0))) +