Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • blockchain/tsa2
  • renn_um/tsa2
2 results
Show changes
Commits on Source (13)
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 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 @@
// 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()
......@@ -11,7 +11,7 @@
<noscript>
<strong>We're sorry but ui doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app" apiurl="http://localhost:3000"></div>
<div id="app" apiurl="http://localhost:3001"></div>
<!-- built files will be auto injected -->
</body>
</html>