Skip to content
Snippets Groups Projects
Commit 1e6a0778 authored by Andrés Blanco's avatar Andrés Blanco
Browse files

Mejoras para poder comunicarse con BFA

Debido a la recomendación de geth de no unlockear las cuentas se
tuvieron que tomar ciertas medidas para poder bypassear esa necesidad.
Básicamente se parametrizó la cuenta y password a utilizar.
parent a0c5bb54
No related branches found
No related tags found
No related merge requests found
847e7d6ea18a417496518dc90b547438bf1b3d05.json
...@@ -28,6 +28,11 @@ El proyecto está compuesto por tres componentes, los contratos, una api rest ...@@ -28,6 +28,11 @@ El proyecto está compuesto por tres componentes, los contratos, una api rest
y una interfaz gráfica. y una interfaz gráfica.
### Deployar los contratos ### Deployar los contratos
La primera vez hay que ejecutar lo siguiente dentro del directorio ```contract```
```shell
npm install
```
Para manejar los contratos se utilizó truffle, para hacer el deploy de los Para manejar los contratos se utilizó truffle, para hacer el deploy de los
contratos hay que correr dentro del directorio ```contract``` el siguiente comando contratos hay que correr dentro del directorio ```contract``` el siguiente comando
```shell ```shell
...@@ -119,13 +124,14 @@ node dist/index.js ...@@ -119,13 +124,14 @@ node dist/index.js
Se pueden definir las siguientes variables de entorno para parametrizar el servidor: Se pueden definir las siguientes variables de entorno para parametrizar el servidor:
* **GETH_HOST**: url del host geth a conectarse. Por defecto ***http://localhost:7545*** * **GETH_HOST**: url del host geth a conectarse. Por defecto ***http://localhost:7545***
* **GETH_ACCOUNT**: account a utilizar. Por defecto se utiliza la que está la * **GETH_ACCOUNT_JSON**: Path a la key encriptada (V3) donde se encuentra la account
pos 0 de ```web3.eth.getAccounts()``` a utilizar. Por defecto se utiliza la que está la pos 0 de ```web3.eth.getAccounts()```
* **GETH_ACCOUNT_PASSWORD**: Clave plana de la key provista en ***GETH_ACCOUNT_JSON***
* **CONTRACT_ABI_PATH**: path al archivo que contiene el abi. Puede ser un path * **CONTRACT_ABI_PATH**: path al archivo que contiene el abi. Puede ser un path
absoluto. absoluto.
Si es relativo la ruta se calcula desde el dir api/dist. Por defecto se trata Si es relativo la ruta se calcula desde el dir api/dist. Por defecto se trata
de cargar desde el directorio build de truffle (```contract/build/contracts/Stamper.json```) de cargar desde el directorio build de truffle (```contract/build/contracts/Stamper.json```)
* **CONTRACT_ABI_ADDRESS**: dirección del contrato. Por defecto se utiliza la * **CONTRACT_ADDRESS**: dirección del contrato. Por defecto se utiliza la
que está en el archivo de build de truffle para el netId actual que está en el archivo de build de truffle para el netId actual
* **USE_CORS**: permite des/habilitar el CORS a este server. Por defecto está * **USE_CORS**: permite des/habilitar el CORS a este server. Por defecto está
habilitado. Para deshabilitar pasar ***USE_CORS=0*** habilitado. Para deshabilitar pasar ***USE_CORS=0***
...@@ -142,10 +148,20 @@ Se pueden definir las siguientes variables de entorno para parametrizar el servi ...@@ -142,10 +148,20 @@ Se pueden definir las siguientes variables de entorno para parametrizar el servi
``` ```
La otra forma es setearlos al correr node, eg: La otra forma es setearlos al correr node, eg:
```shell ```s
PORT=8010 USE_CORS=0 node dist/index.js PORT=8010 USE_CORS=0 node dist/index.js
``` ```
Ejemplo que levanta corriendo contra BFA:
```s
GETH_ACCOUNT_JSON=$(pwd)/../../847e7d6ea18a417496518dc90b547438bf1b3d05.json \
GETH_ACCOUNT_PASSWORD=mipasswordseguro \
GETH_HOST=http://localhost:8545 \
CONTRACT_ABI_PATH=$(pwd)/abi.json \
CONTRACT_ADDRESS=0x7e56220069CAaF8367EA42817EA9210296AeC7c6 \
node dist/index.js
```
### Deploy de la UI ### Deploy de la UI
Al buildear se crea el archivo ```ui/dist/index.html``` y todo el resto de los recursos Al buildear se crea el archivo ```ui/dist/index.html``` y todo el resto de los recursos
necesarios. Al acceder al index.html sólo se ve el componente de Stampeo. El html se ve así: necesarios. Al acceder al index.html sólo se ve el componente de Stampeo. El html se ve así:
......
# Ejemplo en BFA
```bash
cd api
GETH_ACCOUNT_JSON=$(pwd)/../../847e7d6ea18a417496518dc90b547438bf1b3d05.json \
GETH_ACCOUNT_PASSWORD=passwordseguro \
GETH_HOST=http://localhost:8545 \
CONTRACT_ABI_PATH=$(pwd)/abi.json \
CONTRACT_ADDRESS=0x7e56220069CAaF8367EA42817EA9210296AeC7c6 \
npm run serve
```
\ No newline at end of file
[
{
"inputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor",
"signature": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "from",
"type": "address"
},
{
"indexed": true,
"name": "object",
"type": "bytes32"
},
{
"indexed": false,
"name": "blockNo",
"type": "uint256"
}
],
"name": "Stamped",
"type": "event",
"signature": "0x3a0b5d7066b49e6f19e8199e84c7a296092240386482cc1b23a7c4e3c98a79d5"
},
{
"constant": false,
"inputs": [
{
"name": "objectList",
"type": "bytes32[]"
}
],
"name": "put",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0x3a00faae"
},
{
"constant": true,
"inputs": [
{
"name": "pos",
"type": "uint256"
}
],
"name": "getStamplistPos",
"outputs": [
{
"name": "",
"type": "bytes32"
},
{
"name": "",
"type": "address"
},
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x9d192428"
},
{
"constant": true,
"inputs": [
{
"name": "object",
"type": "bytes32"
}
],
"name": "getObjectCount",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xc2433cd3"
},
{
"constant": true,
"inputs": [
{
"name": "object",
"type": "bytes32"
},
{
"name": "pos",
"type": "uint256"
}
],
"name": "getObjectPos",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x789bbb41"
},
{
"constant": true,
"inputs": [
{
"name": "object",
"type": "bytes32"
},
{
"name": "stamper",
"type": "address"
}
],
"name": "getBlockNo",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xbbc48b7c"
},
{
"constant": true,
"inputs": [
{
"name": "stamper",
"type": "address"
}
],
"name": "getStamperCount",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xc15ed491"
},
{
"constant": true,
"inputs": [
{
"name": "stamper",
"type": "address"
},
{
"name": "pos",
"type": "uint256"
}
],
"name": "getStamperPos",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xa08d1a4f"
}
]
\ No newline at end of file
import { sign } from "crypto";
//const stamperInterface = require('../../contract/build/contracts/Stamper.json') //const stamperInterface = require('../../contract/build/contracts/Stamper.json')
class Stamper { class Stamper {
constructor(web3, contractAbi, contractAddress) { constructor(web3, contractAbi, contractAddress) {
this.web3 = web3 this.web3 = web3
this.contractAddress = contractAddress
this.contract = new web3.eth.Contract(contractAbi, contractAddress) this.contract = new web3.eth.Contract(contractAbi, contractAddress)
} }
setSender(fromAddress) { // stampea un conjunto de hashes.
this.from = fromAddress // si walletAccount es undefined trata de usar la account de web3.eth.defaultAccount
} async stamp(hashes, walletAccount) {
async stamp(hashes) {
console.log(`stamping ${hashes}`) console.log(`stamping ${hashes}`)
let defaultAccount = (walletAccount) ? walletAccount.address : this.web3.eth.defaultAccount
let hashesToStamp = [] let hashesToStamp = []
for (let i=0; i < hashes.length; i++) { for (let i=0; i < hashes.length; i++) {
let blockNo = await this.contract.methods.getBlockNo(hashes[i], this.from).call() let blockNo = await this.contract.methods.getBlockNo(hashes[i], defaultAccount).call()
if (blockNo == 0) hashesToStamp.push(hashes[i]) if (blockNo == 0) hashesToStamp.push(hashes[i])
} }
...@@ -25,10 +27,36 @@ class Stamper { ...@@ -25,10 +27,36 @@ class Stamper {
resolve() resolve()
}) })
let txPromise = this.contract.methods.put(hashesToStamp).send({ let txPromise
from: this.from, let gasLimit = 2000000
gasLimit: 500000
}) if (walletAccount) {
let methodPut = this.contract.methods.put(hashesToStamp)
let encodedABI = methodPut.encodeABI()
let tx = {
to: this.contractAddress,
// v: 47525974938 * 35 + 2,
// v: 47525974938,
chainId: '200941592',
gas: gasLimit,
// gasLimit: gasLimit,
data: encodedABI
}
// tx.v = Buffer.from([47525974938])
// tx.nonce = this.web3.utils.toHex(await this.web3.eth.getTransactionCount(defaultAccount))
let signedTx = await walletAccount.signTransaction(tx)
// console.log(signedTx)
// txPromise = this.web3.eth.sendSignedTransaction(signedTx)
// txPromise = this.web3.eth.sendSignedTransaction('0x' + signedTx.serialize().toString('hex'))
txPromise = this.web3.eth.sendSignedTransaction(signedTx.rawTransaction)
} else {
txPromise = this.contract.methods.put(hashesToStamp).send({
from: defaultAccount,
gasLimit: gasLimit
})
}
txPromise.then((receipt) => { txPromise.then((receipt) => {
console.log(`> hashes stampeados en bloque: ${receipt.blockNumber}`) console.log(`> hashes stampeados en bloque: ${receipt.blockNumber}`)
......
...@@ -5,17 +5,15 @@ import express from 'express' ...@@ -5,17 +5,15 @@ import express from 'express'
import cors from 'cors' import cors from 'cors'
import bodyParser from 'body-parser' import bodyParser from 'body-parser'
import basicAuth from 'express-basic-auth' import basicAuth from 'express-basic-auth'
//import SimpleStamper from './simpleStamperWrapper'
import Stamper from './StamperWrapper' import Stamper from './StamperWrapper'
import fs from 'fs'
/***************************************************/ /***************************************************/
// Conexión al provider // Conexión al provider
/***************************************************/ /***************************************************/
const providerHost = process.env.GETH_HOST || 'http://localhost:7545' const providerHost = process.env.GETH_HOST || 'http://localhost:7545'
const accountIsSet = process.env.GETH_ACCOUNT || false // const providerHost = process.env.GETH_HOST || 'http://localhost:8545'
// si no se seteó una account se usa esta const como el indice de accounts de ganache const accountIsSet = process.env.GETH_ACCOUNT_JSON || false
const account = (accountIsSet) ? process.env.GETH_ACCOUNT : 0
var web3 = web3 var web3 = web3
if (typeof web3 !== 'undefined') { if (typeof web3 !== 'undefined') {
...@@ -28,16 +26,48 @@ if (typeof web3 !== 'undefined') { ...@@ -28,16 +26,48 @@ if (typeof web3 !== 'undefined') {
setupWeb3() setupWeb3()
} }
let contractAbi;
let contractAddress;
let walletAccount;
async function setupWeb3() { async function setupWeb3() {
try { try {
let netIsListening = await web3.eth.net.isListening() let netIsListening = await web3.eth.net.isListening()
let netId = await web3.eth.net.getId() let netId = await web3.eth.net.getId()
web3.eth.defaultAccount = (accountIsSet) ? account : (await web3.eth.getAccounts())[account] if (accountIsSet) {
let rawKeyJsonV3 = fs.readFileSync(process.env.GETH_ACCOUNT_JSON)
let keyJsonV3 = JSON.parse(rawKeyJsonV3)
let key = web3.eth.accounts.decrypt(keyJsonV3, process.env.GETH_ACCOUNT_PASSWORD)
walletAccount = web3.eth.accounts.wallet.add(key)
web3.eth.defaultAccount = (await web3.eth.getAccounts())[0]
} else {
// se trata de utilizar una que haya abierta
web3.eth.defaultAccount = (await web3.eth.getAccounts())[0]
}
/***************************************************/
// Carga de contrato
/***************************************************/
if (process.env.CONTRACT_ABI_PATH) {
contractAbi = require(process.env.CONTRACT_ABI_PATH)
if (!process.env.CONTRACT_ADDRESS) {
console.error('Si se especifica el path de un abi, debe proveerse un address con la env CONTRACT_ADDRESS')
process.exit(1)
}
contractAddress = process.env.CONTRACT_ADDRESS
} else {
let path = '../../contract/build/contracts/Stamper.json'
console.log(`Intentando cargar ${path} netId: ${netId}`)
let data = require(path)
contractAbi = data.abi
contractAddress = data.networks[netId].address
}
console.log(`Conectado exitosamente: console.log(`Conectado exitosamente:
> host: ${providerHost} > host: ${providerHost}
> netId: ${netId} > netId: ${netId}
> account: ${web3.eth.defaultAccount} > account: ${web3.eth.defaultAccount}
> address: ${contractAddress}
`) `)
} catch (e) { } catch (e) {
console.error('Error de conexión') console.error('Error de conexión')
...@@ -47,29 +77,7 @@ async function setupWeb3() { ...@@ -47,29 +77,7 @@ async function setupWeb3() {
} }
/***************************************************/ /***************************************************/
// Carga de contrato // Setup CORS y Auth
/***************************************************/
let contractAbi;
let contractAddress;
if (process.env.CONTRACT_ABI_PATH) {
contractAbi = require(process.env.CONTRACT_ABI_PATH)
if (!process.env.CONTRACT_ADDRESS) {
console.error('Si se especifica el path de un abi, debe proveerse un address con la env CONTRACT_ADDRESS')
process.exit(1)
}
contractAddress = process.env.CONTRACT_ADDRESS
} else {
let path = '../../contract/build/contracts/Stamper.json'
console.log(`Intentando cargar ${path}`)
let data = require(path)
contractAbi = data.abi
web3.eth.net.getId().then(function(netId) {
contractAddress = data.networks[netId].address
})
}
/***************************************************/
// Setup API
/***************************************************/ /***************************************************/
const app = express() const app = express()
...@@ -96,7 +104,6 @@ if (process.env.API_USER && process.env.API_PASS) { ...@@ -96,7 +104,6 @@ if (process.env.API_USER && process.env.API_PASS) {
/***************************************************/ /***************************************************/
app.post('/stamp', async (req, res) => { app.post('/stamp', async (req, res) => {
let ss = new Stamper(web3, contractAbi, contractAddress) let ss = new Stamper(web3, contractAbi, contractAddress)
ss.setSender(web3.eth.defaultAccount)
if (!("hashes" in req.body)) { if (!("hashes" in req.body)) {
res.status(422) res.status(422)
...@@ -120,7 +127,7 @@ app.post('/stamp', async (req, res) => { ...@@ -120,7 +127,7 @@ app.post('/stamp', async (req, res) => {
} }
try { try {
let txHash = await ss.stamp(hashes) let txHash = await ss.stamp(hashes, walletAccount)
//let fullUrl = req.protocol + '://' + req.get('host') //let fullUrl = req.protocol + '://' + req.get('host')
res.status(200).send('success') res.status(200).send('success')
} catch (e) { } catch (e) {
...@@ -132,7 +139,6 @@ app.post('/stamp', async (req, res) => { ...@@ -132,7 +139,6 @@ app.post('/stamp', async (req, res) => {
app.get('/verify/:hash', async (req, res) => { app.get('/verify/:hash', async (req, res) => {
let ss = new Stamper(web3, contractAbi, contractAddress) let ss = new Stamper(web3, contractAbi, contractAddress)
ss.setSender(web3.eth.defaultAccount)
var value = req.params.hash var value = req.params.hash
if (! value.startsWith('0x')) { if (! value.startsWith('0x')) {
......
.DS_Store .DS_Store
build build
node_modules
# Deployando a BFA
Para hacer esto tuve que utilizar truffle-hdwallet-provider. Esto fue debido a
la imposibilidad de unlockear la account en geth directamente por un cambio reciente.
Para elegir la account que se quiere utilizar hay que pasar la pkey de la account en la variable
de entorno ```GETH_ACCOUNT_PKEY```.
La invocación se hace así *esto deployea a bfa*
```bash
GETH_ACCOUNT_PKEY=840e5ddddda6037390329b2d0b65f422f2555555555b6de292cc574fff212345 truffle migrate --network bfa
```
Para obtener la pkey desde el keystore de geth seguí las siguiente instrucciones: https://ethereum.stackexchange.com/questions/12830/how-to-get-private-key-from-account-address-and-password
Para hacerlo desde el REPL esto ayudó: https://stackoverflow.com/questions/18260605/how-to-require-node-module-in-node-repl-without-installing-globally
const Migrations = artifacts.require("Migrations"); const Migrations = artifacts.require("Migrations");
module.exports = function(deployer) { module.exports = function(deployer, network) {
deployer.deploy(Migrations); deployer.deploy(Migrations);
}; };
const Stamper = artifacts.require("Stamper"); const Stamper = artifacts.require("Stamper");
module.exports = function(deployer, network) {
module.exports = function(deployer) {
deployer.deploy(Stamper); deployer.deploy(Stamper);
}; };
This diff is collapsed.
{
"name": "tsa2-contract",
"version": "1.0.0",
"description": "",
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"truffle-hdwallet-provider": "^1.0.9"
}
}
...@@ -21,9 +21,10 @@ ...@@ -21,9 +21,10 @@
// const HDWalletProvider = require('truffle-hdwallet-provider'); // const HDWalletProvider = require('truffle-hdwallet-provider');
// const infuraKey = "fj4jll3k....."; // const infuraKey = "fj4jll3k.....";
// //
// const fs = require('fs');
// const mnemonic = fs.readFileSync(".secret").toString().trim(); // const mnemonic = fs.readFileSync(".secret").toString().trim();
const HDWalletProvider = require("truffle-hdwallet-provider");
module.exports = { module.exports = {
/** /**
* Networks define how you connect to your ethereum client and let you set the * Networks define how you connect to your ethereum client and let you set the
...@@ -36,6 +37,12 @@ module.exports = { ...@@ -36,6 +37,12 @@ module.exports = {
*/ */
networks: { networks: {
bfa: {
host: "127.0.0.1", // Localhost (default: none)
port: 8545, // Standard Ethereum port (default: none)
provider: () => new HDWalletProvider(process.env.GETH_ACCOUNT_PKEY, "http://127.0.0.1:8545"),
network_id: "47525974938", // Any network (default: none)
}
// Useful for testing. The `development` name is special - truffle uses it by default // Useful for testing. The `development` name is special - truffle uses it by default
// if it's defined here and no other network is specified at the command line. // if it's defined here and no other network is specified at the command line.
// You should run a client (like ganache-cli, geth or parity) in a separate terminal // You should run a client (like ganache-cli, geth or parity) in a separate terminal
...@@ -97,3 +104,5 @@ module.exports = { ...@@ -97,3 +104,5 @@ module.exports = {
} }
} }
} }
//provider.engine.stop()
\ No newline at end of file
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<noscript> <noscript>
<strong>We're sorry but ui doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> <strong>We're sorry but ui doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript> </noscript>
<div id="app" apiurl="http://localhost:3000"></div> <div id="app" apiurl="http://localhost:3001"></div>
<!-- built files will be auto injected --> <!-- built files will be auto injected -->
</body> </body>
</html> </html>
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