Inicio » Grupo MADφ » Taller International Conference on Regional Science

Taller International Conference on Regional Science

En esta página puedes encontrar las códigos en Solidity (de Ethereum) de un contrato inteligente para poder compilar y desplegar un sistema de voto en un simulador de blockchain (vía Ganache js). La prueba del resultado se puede hacer vía consola o a través de navegador (con los códigos html y js).

Instalación de los paquetes necesarios

sudo apt-get update
curl -sL https://deb.nodesource.com/setup_7.x -o nodesource_setup.sh
sudo bash nodesource_setup.sh
sudo apt-get install nodejs
node --version
npm --version mkdir -p tallerUPV
cd tallerUPV
npm install ganache-cli web3@0.20.1  solc@0.4.23
node_modules/.bin/ganache-cli

Ya tenemos las 10 cuentas de Ethereum

Available Accounts
==================
(0) 0x2c7f38aca10ec0fd14ed7f35af3ad39c913b8d64
(1) 0x2ae9d31105deed46940aa02a9de6d0d150ee6226
(2) 0xeb7e61354d0617048299141bc6a1153888a2dc69
(3) 0x72d88e13efd3f19e93bee8b0ce02963a5f66db53
(4) 0x085aea284a40fa25eb4ae218e1ee5c943ee62260
(5) 0xcece14d68ca6263d7b647bd1405e17d447a4c680
(6) 0x4041074e2ad96f726ce900fd260ca78f175f3305
(7) 0x49ac44fb83b1665b159a306f01db67cd3e3b0a91
(8) 0xd6b289ae3a3c5bc16ad969cd4e668a6a591848e6
(9) 0x639a91e56f09ca7037ed25ecb2f3070704701af4
Private Keys
==================
(0) e3b8caba48c0cfe7566fadf2e3b860f84f6c03c4afc081faa3e2860839aadcb2
(1) 9a0b5d7cc2f456a09c9437a8b7d23c99451f1a39d4da4727c7996fcd1f6ffb7b
(2) 4d1743ff5922d11615337db0f5c5029aa9727a4fd41e3e3f0f66eea800204751
(3) 9e37a55e14ff76058f0ccb4bb917fd71cd1dab18c5f306328a195813c8d15a39
(4) 12f34bcbaa1578b8fd4b30ec3d617a6e15dfca2919602c7be5f98c3e33770661
(5) d2fcaec8a58bebdb4b9591f0416eaf883fd001fd3d634482a8eb8d4548ec9f20
(6) b8b1e69a3408c2ea2b57f6491bd810b3097fb63ad85f23354b39bf0eafe563f5
(7) 72fe6c02eb15fb86855896933cb827ceae5b2a5064a01c3fdd62196fb43bd61c
(8) 2f6d10d0a2bba1678efff1090722e848ee139bf4e6eae6e96a692d3efc59f7a2
(9) d3529a058c1b239125e0ad415141c8480cc8c4b40810271ac498d759dc9362fc

Contrato inteligente: VotoUPV.sol

pragma solidity ^0.4.18;
contract VotoUPV {
  mapping (bytes32 => uint8) public votosRecibidos;
   bytes32[] public listaCandidatos;
  function VotoUPV(bytes32[] nombreCandidatos) public {
               listaCandidatos = nombreCandidatos;  }
  function totalVotosPara(bytes32 candidato) view public returns (uint8) {
              require(candidatoValido(candidato));
               return votosRecibidos[candidato];  }
  function votoParaCandidato(bytes32 candidato) public {
               require(candidatoValido(candidato));
               votosRecibidos[candidato] += 1;  }
  function candidatoValido(bytes32 candidato) view public returns (bool) { 
               for(uint i = 0; i < listaCandidatos.length; i++) {
                       if (listaCandidatos[i] == candidato) {
                            return true;}}
                       return false;}}

Compilando el contrato

node
> Web3 = require('web3')  
> web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
> web3.eth.accounts
> code = fs.readFileSync('VotoUPV.sol').toString()  
> solc = require('solc')
> compiledCode = solc.compile(code)

Desplegando el contrato

> abiDefinition = JSON.parse(compiledCode.contracts[':VotoUPV'].interface) 
> VotoUPVContract = web3.eth.contract(abiDefinition) 
> byteCode = compiledCode.contracts[':VotoUPV'].bytecode 
> deployedContract = VotoUPVContract.new(['Herve','Pep Lluis','Jose'],{data: byteCode, from: web3.eth.accounts[0], gas: 4700000}) 
> deployedContract.address
'0xeb85d153f6da07c2a9caa64032fc32a8e8b9067f' <- ESTA SERÁ LA DIRECCIÓN DEL CONTRATO
> contractInstance = VotoUPVContract.at(deployedContract.address)

Probando el contrato...a mano

> contractInstance.totalVotosPara.call('Herve') 
{ [String: '0'] s: 1, e: 0, c: [ 0 ] }
> contractInstance.votoParaCandidato('Herve', {from: web3.eth.accounts[0]})
'0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc786bf53'
> contractInstance.votoParaCandidato('Herve', {from: web3.eth.accounts[0]}) 
'0x02c054d238038d68b65d55770fabfca592a5cf6590229ab91bbe7cd72da46de9'
> contractInstance.votoParaCandidato('Herve', {from: web3.eth.accounts[0]}) 
'0x3da069a09577514f2baaa11bc3015a16edf26aad28dffbcd126bde2e71f2b76f'
> contractInstance.totalVotosPara.call('Herve').toLocaleString() 
'3'

... y desde la web: index.html

<!DOCTYPE html><html>
<head> 
   <title> DApp Asegurando la participación ciudadana</title> 
   <link href='https://fonts.googleapis.com/css?family=Open Sans:400,700' rel='stylesheet' type='text/css'> 
   <link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet' type='text/css'>
</head>
<body class="container"> 
   <h1>DApp Asegurando la participación ciudadana con BlockChain. TechFest 2018 UPV</h1> 
   <div class="table-responsive">  <table class="table table-bordered">     
      <thead>  <tr>     <th>Candidato</th>     <th>Número de votos</th>       </tr>   </thead>   
      <tbody>    <tr>     <td>Herve</td>       <td id="candidate-1"></td>    </tr>    
                  <tr>     <td>Pep Lluis</td>   <td id="candidate-2"></td>    </tr>    
                  <tr>     <td>Jose</td>        <td id="candidate-3"></td>    </tr>   
   </tbody>  </table> 
   </div> <input type="text" id="candidato" /> 
   <a href="#" onclick="votoParaCandidato()" class="btn btn-primary">Pincha para votar</a>
</body>
<script src="https://cdn.rawgit.com/ethereum/web3.js/develop/dist/web3.js"></script>
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
<script src="./index.js"></script>
</html>

...index.js

web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
abi = JSON.parse('
   [{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"listaCandidatos","outputs": 
   [{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},
     {"constant":false,"inputs": 
   [{"name":"candidato","type":"bytes32"}],"name":"votoParaCandidato","outputs": 
   [],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs": 
   [{"name":"candidato","type":"bytes32"}],"name":"totalVotosPara","outputs": 
   [{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs": 
   [{"name":"candidato","type":"bytes32"}],"name":"candidatoValido","outputs": 
   [{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs": 
   [{"name":"","type":"bytes32"}],"name":"votosRecibidos","outputs": 
   [{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs": 
   [{"name":"nombreCandidatos","type":"bytes32[]"}],"payable":false,"stateMutability":
       "nonpayable","type":"constructor"}]')

VotoUPVContract = web3.eth.contract(abi); 
contractInstance = VotoUPVContract.at('0xeb85d153f6da07c2a9caa64032fc32a8e8b9067f'); 
candidatos = {"Herve": "candidate-1", "Pep Lluis": "candidate-2", "Jose": "candidate-3"} 

function votoParaCandidato() {    
   nombreCandidato = $("#candidato").val();    
   contractInstance.votoParaCandidato(nombreCandidato, 
   {from: web3.eth.accounts[0]}, function() {    
   let div_id = candidatos[nombreCandidato];    
   $("#" + div_id).html(contractInstance.totalVotosPara.call(nombreCandidato).toString());}); 

$(document).ready(function() {    
   nombreCandidatos = Object.keys(candidatos); 
   for (var i = 0; i < nombreCandidatos.length; i++) {
   let name = nombreCandidatos[i]
   let val = contractInstance.totalVotosPara.call(name).toString()
   $("#"+candidatos[name]).html(val); } });