Commit 1f660f7e authored by Robert Martin-Legene's avatar Robert Martin-Legene

Merge branch 'develop' into 'markup'

# Conflicts:
#   ui/public/index.html
parents 7b49e49b 8914cc8a
847e7d6ea18a417496518dc90b547438bf1b3d05.json
# TSA2
Descargar el proyecto:
```shell
git clone https://gitlab.bfa.ar/blockchain/tsa2.git
```
Actualmente el contrato se encuentra deployado en BFA:
* dirección: **0x7e56220069CAaF8367EA42817EA9210296AeC7c6**
* abi: https://gitlab.bfa.ar/blockchain/tsa2/blob/develop/api/abi.json
También está deployado en la testnet2:
* dirección: **0xFc0f01A88bD08b988173A2354952087C9492d947**
* abi: https://gitlab.bfa.ar/blockchain/tsa2/blob/develop/api/abi.json
## Discusión
Cosas que habría que discutir:
......@@ -12,11 +15,11 @@ Cosas que habría que discutir:
* vale la pena ponerse a ahorrar gas? Ahora cada put sale ~150000 GAS
## Dependencias
Todo fue probado con node 10.8 y npm 6.2.
**Todo fue probado con node 10.8 y npm 6.2.**
Si se desea hacer modificaciones en el contrato hay que tener instalado truffle
de manera global:
```shell
```bash
npm install -g truffle
```
......@@ -28,14 +31,19 @@ El proyecto está compuesto por tres componentes, los contratos, una api rest
y una interfaz gráfica.
### Deployar los contratos
La primera vez hay que ejecutar lo siguiente dentro del directorio ```contract```
```bash
npm install
```
Para manejar los contratos se utilizó truffle, para hacer el deploy de los
contratos hay que correr dentro del directorio ```contract``` el siguiente comando
```shell
```bash
truffle migrate
```
Si se hacen modificaciones sobre el contrato se debe ejecutar lo siguiente:
```shell
```bash
truffle migrate --reset
```
......@@ -44,17 +52,17 @@ en ```contract/contracts/Stamper.sol```.
### Correr API
La primera vez hay que ejecutar lo siguiente dentro del directorio ```api```
```shell
```bash
npm install
```
Para que la API quede corriendo hay que ejecutar:
```shell
```bash
npm run serve
```
Si todo fue bien, se debería ver algo así en la consola:
```shell
```bash
➜ api git:(master) ✗ npm run serve
> api@1.0.0 serve /home/ablanco/proyectos/bfa/tsa/api
......@@ -89,12 +97,12 @@ La api tiene dos endpoints:
### UI
La primera vez hay que ejecutar lo siguiente dentro del directorio ```ui```
```shell
```bash
npm install
```
La aplicación está escrita con Vue.js. Para correr el servicio hay que ejecutar:
```shell
```bash
npm run serve
```
## Instalación en producción
......@@ -103,7 +111,7 @@ ya se está listo para correr la API y tirar los assets de UI donde se desee.
### Compilación de componentes
Para compilar la UI y la API hay que ejecutar el comando:
```shell
```bash
npm run build
```
dentro de cada uno de los directorios.
......@@ -113,19 +121,20 @@ Como output se van a crear los directorios ```ui/dist``` y ```api/dist```
### Deploy y configuración de la API
Para correr la API hay que ejecutar con node el archivo ```dist/index.js```
```shell
```bash
node dist/index.js
```
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_ACCOUNT**: account a utilizar. Por defecto se utiliza la que está la
pos 0 de ```web3.eth.getAccounts()```
* **GETH_ACCOUNT_JSON**: Path a la key encriptada (V3) donde se encuentra la account
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
absoluto.
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```)
* **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
* **USE_CORS**: permite des/habilitar el CORS a este server. Por defecto está
habilitado. Para deshabilitar pasar ***USE_CORS=0***
......@@ -142,10 +151,20 @@ Se pueden definir las siguientes variables de entorno para parametrizar el servi
```
La otra forma es setearlos al correr node, eg:
```shell
```bash
PORT=8010 USE_CORS=0 node dist/index.js
```
Ejemplo que levanta corriendo contra BFA:
```bash
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
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í:
......
# Ejemplo en BFA
(en lugar de __UTC*__ poner el nombre completo del archivo de la cuenta)
```bash
cd api
GETH_ACCOUNT_JSON="/home/bfa/bfa/network/node/keystore/UTC*" \
GETH_ACCOUNT_PASSWORD="miclave" \
GETH_HOST=http://localhost:8545 \
CONTRACT_ABI_PATH="$(pwd)/abi.json" \
CONTRACT_ADDRESS="0x7e56220069CAaF8367EA42817EA9210296AeC7c6" \
npm run serve
```
# Ejemplo en test network
(en lugar de __UTC*__ poner el nombre completo del archivo de la cuenta)
```bash
cd api
GETH_ACCOUNT_JSON="/home/bfa/bfa/test2network/node/keystore/UTC*" \
GETH_ACCOUNT_PASSWORD="miclave" \
GETH_HOST=http://localhost:8545 \
CONTRACT_ABI_PATH="$(pwd)/abi.json" \
CONTRACT_ADDRESS="0xFc0f01A88bD08b988173A2354952087C9492d947" \
npm run serve
```
[
{
"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')
class Stamper {
constructor(web3, contractAbi, contractAddress) {
this.web3 = web3
this.contractAddress = contractAddress
this.contract = new web3.eth.Contract(contractAbi, contractAddress)
}
setSender(fromAddress) {
this.from = fromAddress
}
async stamp(hashes) {
// stampea un conjunto de hashes.
// si walletAccount es undefined trata de usar la account de web3.eth.defaultAccount
async stamp(hashes, walletAccount) {
console.log(`stamping ${hashes}`)
let defaultAccount = (walletAccount) ? walletAccount.address : this.web3.eth.defaultAccount
let hashesToStamp = []
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])
}
......@@ -25,10 +27,36 @@ class Stamper {
resolve()
})
let txPromise = this.contract.methods.put(hashesToStamp).send({
from: this.from,
gasLimit: 500000
})
let txPromise
let gasLimit = 2000000
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) => {
console.log(`> hashes stampeados en bloque: ${receipt.blockNumber}`)
......
......@@ -5,17 +5,15 @@ import express from 'express'
import cors from 'cors'
import bodyParser from 'body-parser'
import basicAuth from 'express-basic-auth'
//import SimpleStamper from './simpleStamperWrapper'
import Stamper from './StamperWrapper'
import fs from 'fs'
/***************************************************/
// Conexión al provider
/***************************************************/
const providerHost = process.env.GETH_HOST || 'http://localhost:7545'
const accountIsSet = process.env.GETH_ACCOUNT || false
// si no se seteó una account se usa esta const como el indice de accounts de ganache
const account = (accountIsSet) ? process.env.GETH_ACCOUNT : 0
// const providerHost = process.env.GETH_HOST || 'http://localhost:8545'
const accountIsSet = process.env.GETH_ACCOUNT_JSON || false
var web3 = web3
if (typeof web3 !== 'undefined') {
......@@ -28,16 +26,48 @@ if (typeof web3 !== 'undefined') {
setupWeb3()
}
let contractAbi;
let contractAddress;
let walletAccount;
async function setupWeb3() {
try {
let netIsListening = await web3.eth.net.isListening()
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:
> host: ${providerHost}
> netId: ${netId}
> account: ${web3.eth.defaultAccount}
> address: ${contractAddress}
`)
} catch (e) {
console.error('Error de conexión')
......@@ -47,29 +77,7 @@ async function setupWeb3() {
}
/***************************************************/
// Carga de contrato
/***************************************************/
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
// Setup CORS y Auth
/***************************************************/
const app = express()
......@@ -96,7 +104,6 @@ if (process.env.API_USER && process.env.API_PASS) {
/***************************************************/
app.post('/stamp', async (req, res) => {
let ss = new Stamper(web3, contractAbi, contractAddress)
ss.setSender(web3.eth.defaultAccount)
if (!("hashes" in req.body)) {
res.status(422)
......@@ -120,7 +127,7 @@ app.post('/stamp', async (req, res) => {
}
try {
let txHash = await ss.stamp(hashes)
let txHash = await ss.stamp(hashes, walletAccount)
//let fullUrl = req.protocol + '://' + req.get('host')
res.status(200).send('success')
} catch (e) {
......@@ -132,7 +139,6 @@ app.post('/stamp', async (req, res) => {
app.get('/verify/:hash', async (req, res) => {
let ss = new Stamper(web3, contractAbi, contractAddress)
ss.setSender(web3.eth.defaultAccount)
var value = req.params.hash
if (! value.startsWith('0x')) {
......
.DS_Store
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:
```bash
npm install truffle-hdwallet-provider
```
Luego, hay que obtener la clave privada de la cuenta Ethereum y ponerla en la variable de entorno ```GETH_ACCOUNT_PKEY``` antes de invocar el ```truffle migrate``` a la red.
Para obtener la clave privada hay que seguir las instrucciones en https://ethereum.stackexchange.com/questions/12830/how-to-get-private-key-from-account-address-and-password
Instalar **keythereum** en _otro_ directorio (porque si no se pelea con directorios que ya usa truffle):
```bash
mkdir /tmp/keythereum
cd /tmp/keythereum
npm install keythereum
```
Ubicar la dirección de la cuenta en el archivo que está en ```/home/bfa/bfa/network/node/keystore``` (o ```/home/bfa/bfa/test2network/node/keystore``` si es la red de testing).
Después, invocar a la consola node.js (con el comando ```nodejs```) y ejecutar los siguientes pasos (cambiando _miclave_ por la clave de la cuenta -o el string vacío si la cuenta no tiene clave- y la dirección obtenida arriba en lugar de _MiDireccion_):
```javascript
var keythereum = require("keythereum");
var datadir = "/home/bfa/bfa/network/node"; // si es la red de testing usar "/home/bfa/bfa/test2network/node"
var address= "0xMiDireccion";
const password = "miclave";
var keyObject = keythereum.importFromFile(address, datadir);
var privateKey = keythereum.recover(password, keyObject);
console.log(privateKey.toString('hex'));
```
el ```console.log()``` va a imprimir una clave con este aspecto: **bab415bada55bab415bada55bab415bada55bab415bada55bab415bada55bab4**.
Eso es lo que hay que poner en la variable de entorno ```GETH_ACCOUNT_PKEY```.
La invocación se hace así **esto deployea a bfa**
```bash
GETH_ACCOUNT_PKEY=bab415bada55bab415bada55bab415bada55bab415bada55bab415bada55bab4 truffle migrate --network bfa
```
(Si se quiere deployar a la red de testing utilizar ```--network bfatest2```)
const Migrations = artifacts.require("Migrations");
module.exports = function(deployer) {
module.exports = function(deployer, network) {
deployer.deploy(Migrations);
};
const Stamper = artifacts.require("Stamper");
module.exports = function(deployer) {
module.exports = function(deployer, network) {
deployer.deploy(Stamper);
};
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "tsa2-contract",
"version": "1.0.0",
"description": "",
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"truffle-hdwallet-provider": "^1.0.9"
}
}
......@@ -21,9 +21,10 @@
// const HDWalletProvider = require('truffle-hdwallet-provider');
// const infuraKey = "fj4jll3k.....";
//
// const fs = require('fs');
// const mnemonic = fs.readFileSync(".secret").toString().trim();
const HDWalletProvider = require("truffle-hdwallet-provider");
module.exports = {
/**
* Networks define how you connect to your ethereum client and let you set the
......@@ -36,6 +37,20 @@ module.exports = {
*/
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", // BFA produccion
},
bfatest2: {
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: "55555000000", // BFA produccion
}
// 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.
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
......@@ -97,3 +112,5 @@ module.exports = {
}
}
}
//provider.engine.stop()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment