From bf0334892a5e56aba9fa6c4fd4880fa5e529d503 Mon Sep 17 00:00:00 2001 From: Renzo <rontivero@ultimamillasa.com.ar> Date: Wed, 22 Apr 2020 17:48:20 -0300 Subject: [PATCH] Avances Renzo --- api/controllers/AccountController.js | 41 +++++ api/controllers/BlockchainController.js | 187 +++++++++++++---------- api/controllers/TransactionController.js | 40 +++++ api/helpers/get-block-number.js | 50 ++++++ api/helpers/get-hash.js | 50 ++++++ api/helpers/get-ots.js | 49 ++++++ api/helpers/verify-hash.js | 59 +++++++ api/models/Account.js | 29 ++++ api/models/Transaction.js | 29 ++++ config/custom.js | 3 +- config/routes.js | 7 +- package-lock.json | 10 ++ package.json | 2 + 13 files changed, 472 insertions(+), 84 deletions(-) create mode 100755 api/controllers/AccountController.js create mode 100755 api/controllers/TransactionController.js create mode 100755 api/helpers/get-block-number.js create mode 100755 api/helpers/get-hash.js create mode 100755 api/helpers/get-ots.js create mode 100755 api/helpers/verify-hash.js create mode 100755 api/models/Account.js create mode 100755 api/models/Transaction.js diff --git a/api/controllers/AccountController.js b/api/controllers/AccountController.js new file mode 100755 index 0000000..017cc8d --- /dev/null +++ b/api/controllers/AccountController.js @@ -0,0 +1,41 @@ +/** + * AccountController + * + * @description :: Server-side actions for handling incoming requests. + * @help :: See https://sailsjs.com/docs/concepts/actions + */ + +const Web3 = require('web3'); +const Tx = require('ethereumjs-tx'); +const url = sails.config.custom.urlRpc; +const web3 = new Web3(url); +const accountAddress = sails.config.custom.accountAddress; +const contractABI = sails.config.custom.contractABI; +const contractAddress = sails.config.custom.contractAddress; +const privateKey = Buffer.from( + sails.config.custom.privateKey, + 'hex', +); +const contract = new web3.eth.Contract(contractABI, contractAddress); + +module.exports = { + create : async function (req, res){ + var account_data = web3.eth.accounts.create(); + return res.json(account_data); + }, + + getBalance : async function (req, res){ + var account = req.params.account; + + web3.eth.getBalance(account, (err, bal) => { + if(err){ + return res.json(err.toString()); + } + var balanceToEther = web3.utils.fromWei(bal, 'ether') + return res.json(balanceToEther); + + }); + } + +}; + diff --git a/api/controllers/BlockchainController.js b/api/controllers/BlockchainController.js index 770b68e..8929f91 100755 --- a/api/controllers/BlockchainController.js +++ b/api/controllers/BlockchainController.js @@ -6,6 +6,7 @@ */ const Web3 = require('web3'); const Tx = require('ethereumjs-tx'); +const base64 = require('nodejs-base64-encode'); const url = sails.config.custom.urlRpc; const web3 = new Web3(url); const accountAddress = sails.config.custom.accountAddress; @@ -19,97 +20,127 @@ const contract = new web3.eth.Contract(contractABI, contractAddress); module.exports = { - verify : async function (req, res){ + verify : async function (req, res){ + + const file_hash = req.params.file_hash; + const base64_ots = req.params.ots; + + // Transformo datos + const aux_ots = base64.decode(base64_ots.toString(), 'base64'); + var array_ots = aux_ots.split('-'); + + // OTS definitivo + var permanent_ots; + + if(array_ots.length != 2){ + return res.json('El OTS enviado es inválido'); + } + + const ots = array_ots[0]; // Este es el OpenTimeStamp (OTS) original creado en el método stamp() con el helper getOts(); Es un ID único para cada request. + const tx_hash = array_ots[1]; // Hash de la TX obtenida de la blockchain + + // Verifico si el OTS + Hash enviado son válidos + const result_verify = await sails.helpers.verifyHash.with({ + ots: ots, + file_hash: file_hash, + }); - contract.methods.verify(req.params.ots,req.params.file_hash).call({from: accountAddress}, (err, result) => { - if(err){ - return res.json(err) - } + + if(result_verify){ + + const block_number = await sails.helpers.getBlockNumber(ots); + + return res.json({ + status : 'success', + tx_hash : tx_hash, + block_number : block_number, + file_hash : file_hash, + ots : ots, + }); + } else { // Verifico si la Transacción aún no se ha incluido en un bloque + + // TODO: Mover la funcion al controlador correspondiente + var new_tx_hash = await sails.helpers.getHash(ots); + sails.log(new_tx_hash) + // Si el + if(tx_hash != new_tx_hash){ + return res.json({ + status : 'fail', + file_hash_by_ots : new_tx_hash, + file_hash_send : file_hash, + tx_hash : tx_hash, + ots : ots, + msg : 'El HASH del archivo enviado no se corresponde con el OTS.' + }); + } - return res.json(result); - }); + // Analizar el tema del estado pendiente de las TX + /*var tx = await web3.eth.getTransaction(tx_hash, (err, _tx) => { + if(err){ + return res.json(err.toString()); + } + + // Significa que la TX aún no se incluye en un bloque. Notificamos para que intente más tarde + if(!_tx.block_number){ + return res.json({ + status : 'pending', + tx_hash : tx_hash, + file_hash : file_hash, + ots : ots, + }); + } + + });*/ + } }, stamp : async function (req, res){ - var _ots = req.body.ots; - var _file_hash = req.body.file_hash; - - // TODO: antes de stampar el hash recibido, verificar si ya fue estampado anteriormente - - web3.eth.getTransactionCount(accountAddress, (err, txCount) => { - - const data = contract.methods.stamp(_ots, _file_hash).encodeABI(); - - // Construir la transaccion - const txObject = { - nonce: web3.utils.toHex(txCount), - to: contractAddress, - gasLimit: web3.utils.toHex(800000), - gasPrice: web3.utils.toHex(web3.utils.toWei('2000', 'gwei')), - data: data - } - - // Firmar la transaccion - const tx = new Tx(txObject); - tx.sign(privateKey); - - const serializeTransaction = tx.serialize(); - const raw = '0x' + serializeTransaction.toString('hex'); - - // Transmitir la transacción - web3.eth.sendSignedTransaction(raw, (err, txHash) => { - if(err){ - return res.json(err); + const file_hash = req.body.file_hash; + // A partir del Hash recibido, genero el OpenTimeStamp (OTS) + const ots = await sails.helpers.getOts(file_hash); + var comprobante_ots; + + web3.eth.getTransactionCount(accountAddress, (err, txCount) => { + + const data = contract.methods.stamp(ots, file_hash).encodeABI(); + + // Construir la transaccion + const txObject = { + nonce: web3.utils.toHex(txCount), + to: contractAddress, + gasLimit: web3.utils.toHex(800000), + // TODO: revisar que el precio sea automático + gasPrice: web3.utils.toHex(web3.utils.toWei('1000', 'gwei')), + data: data } - return res.json(txHash); - }); - }); - }, - - getBlockNumber : async function (req, res){ - var _ots = req.params.ots; + // Firmar la transaccion + const tx = new Tx(txObject); + tx.sign(privateKey); - contract.methods.getBlockNumber(_ots).call((err, result) => { - if(err){ - return res.json(err); - } + const serializeTransaction = tx.serialize(); + const raw = '0x' + serializeTransaction.toString('hex'); - return res.json(result); - }); - }, + // Transmitir la transacción + web3.eth.sendSignedTransaction(raw, (err, tx_hash) => { + + if(err){ + return res.json(err.toString()); + } - getHash : async function (req, res){ - var _ots = req.params.ots; + comprobante_ots = ots + '-' + tx_hash; - contract.methods.getHash(_ots).call((err, result) => { - if(err){ - return res.json(err); - } - - return res.json(result); - }); - }, + // Si está todo bien, retorno el OpenTimeStamp definitivo para luego comprobar si el hash del archivo junto con este comprobante son válidos + comprobante_ots = base64.encode(comprobante_ots.toString(), 'base64'); - createAccount : async function (req, res){ - var account_data = web3.eth.accounts.create(); - return res.json(account_data); + return res.json({ + comprobante_ots : comprobante_ots, + tx_hash : tx_hash + }); + }); + }); + }, - - sendTransaction: async function(req, res){ - var _from = req.body.from; - var _to = req.body.to; - var _ether = req.body.ether; - - web3.eth.sendTransaction({ from: _from, to: _to, value: web3.utils.toWei(_ether, 'ether')}, (err, txHash) => { - if(err){ - return res.json(err); - } - - return res.json(txHash); - }); - } - }; diff --git a/api/controllers/TransactionController.js b/api/controllers/TransactionController.js new file mode 100755 index 0000000..8a3afb3 --- /dev/null +++ b/api/controllers/TransactionController.js @@ -0,0 +1,40 @@ +/** + * TransactionController + * + * @description :: Server-side actions for handling incoming requests. + * @help :: See https://sailsjs.com/docs/concepts/actions + */ + + const Web3 = require('web3'); + const Tx = require('ethereumjs-tx'); + const url = sails.config.custom.urlRpc; + const web3 = new Web3(url); + const accountAddress = sails.config.custom.accountAddress; + const contractABI = sails.config.custom.contractABI; + const contractAddress = sails.config.custom.contractAddress; + const privateKey = Buffer.from( + sails.config.custom.privateKey, + 'hex', + ); + const contract = new web3.eth.Contract(contractABI, contractAddress); + + module.exports = { + + send: async function(req, res){ + var _from = req.body.from; + var _to = req.body.to; + var _ether = req.body.ether; + + web3.eth.sendTransaction({ from: _from, to: _to, value: web3.utils.toWei(_ether, 'ether')}, (err, txHash) => { + if(err){ + return res.json(err.toString()); + } + + return res.json(txHash); + }); + }, + + + +}; + diff --git a/api/helpers/get-block-number.js b/api/helpers/get-block-number.js new file mode 100755 index 0000000..8d37e2f --- /dev/null +++ b/api/helpers/get-block-number.js @@ -0,0 +1,50 @@ +const Web3 = require('web3'); +const url = sails.config.custom.urlRpc; +const web3 = new Web3(url); +const contractABI = sails.config.custom.contractABI; +const contractAddress = sails.config.custom.contractAddress; +const contract = new web3.eth.Contract(contractABI, contractAddress); + +module.exports = { + + + friendlyName: 'Get block number', + + + description: '', + + + inputs: { + ots : { + type: 'string', + require: true, + }, + }, + + + exits: { + + success: { + outputFriendlyName: 'Block number', + }, + + }, + + + fn: async function (inputs) { + + var result = await contract.methods.getBlockNumber(inputs.ots).call((err, result) => { + if(err){ + return err.toString(); + } + + return result; + }); + + return result; + + } + + +}; + diff --git a/api/helpers/get-hash.js b/api/helpers/get-hash.js new file mode 100755 index 0000000..608ac4b --- /dev/null +++ b/api/helpers/get-hash.js @@ -0,0 +1,50 @@ +const Web3 = require('web3'); +const url = sails.config.custom.urlRpc; +const web3 = new Web3(url); +const contractABI = sails.config.custom.contractABI; +const contractAddress = sails.config.custom.contractAddress; +const contract = new web3.eth.Contract(contractABI, contractAddress); + +module.exports = { + + + friendlyName: 'Get hash', + + + description: '', + + + inputs: { + ots : { + type: 'string', + require: true, + }, + }, + + + exits: { + + success: { + outputFriendlyName: 'Hash', + }, + + }, + + + fn: async function (inputs) { + + var result = await contract.methods.getHash(inputs.ots).call((err, result) => { + if(err){ + return err.toString(); + } + + return result; + }); + + return result; + + } + + +}; + diff --git a/api/helpers/get-ots.js b/api/helpers/get-ots.js new file mode 100755 index 0000000..871790e --- /dev/null +++ b/api/helpers/get-ots.js @@ -0,0 +1,49 @@ +const sha256 = require('js-sha256'); + +module.exports = { + + + friendlyName: 'Get ots', + + + description: '', + + + inputs: { + file_hash : { + type : 'string', + required : true + } + }, + + + exits: { + + success: { + outputFriendlyName: 'Ots', + }, + + }, + + + fn: async function (inputs) { + + var ots; + const file_hash = inputs.file_hash; + const accountAddress = sails.config.custom.accountAddress; + const contractAddress = sails.config.custom.contractAddress; + const now = new Date(); + const timeStamp = now.getTime(); + + + var hash = sha256.create(); + hash.update(file_hash.toString() + accountAddress.toString() + contractAddress.toString() + timeStamp.toString()); + ots = hash.hex(); + + return ots; + + } + + +}; + diff --git a/api/helpers/verify-hash.js b/api/helpers/verify-hash.js new file mode 100755 index 0000000..e2c4e9f --- /dev/null +++ b/api/helpers/verify-hash.js @@ -0,0 +1,59 @@ +const Web3 = require('web3'); +const Tx = require('ethereumjs-tx'); +const url = sails.config.custom.urlRpc; +const web3 = new Web3(url); +const accountAddress = sails.config.custom.accountAddress; +const contractABI = sails.config.custom.contractABI; +const contractAddress = sails.config.custom.contractAddress; +const privateKey = Buffer.from( + sails.config.custom.privateKey, + 'hex', +); +const contract = new web3.eth.Contract(contractABI, contractAddress); + +module.exports = { + + + friendlyName: 'Verify hash', + + + description: '', + + + inputs: { + ots : { + type: 'string', + require: true, + }, + + file_hash : { + type : 'string', + require : true + } + }, + + + exits: { + + success: { + description: 'All done.', + }, + + }, + + + fn: async function (inputs) { + + var result = await contract.methods.verify(inputs.ots,inputs.file_hash).call({from: accountAddress}, (err, result) => { + if(err){ + return err.toString(); + } + return result; + }); + + return result; + } + + +}; + diff --git a/api/models/Account.js b/api/models/Account.js new file mode 100755 index 0000000..d0285ec --- /dev/null +++ b/api/models/Account.js @@ -0,0 +1,29 @@ +/** + * Account.js + * + * @description :: A model definition represents a database table/collection. + * @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models + */ + +module.exports = { + + attributes: { + + // â•”â•╗╦â•╗╦╔╦╗╦╔╦╗╦╦ ╦╔â•â•—â•”â•â•— + // â• â•â•╠╦â•â•‘â•‘â•‘â•‘â•‘ â•‘ ║╚╗╔â•â•‘â•£ ╚â•â•— + // â•© ╩╚â•â•©â•© â•©â•© â•© â•© ╚╠╚â•â•╚â•â• + + + // â•”â•╗╔╦╗╔╗ â•”â•╗╔╦╗╔â•â•— + // â•‘â•£ â•‘â•‘â•‘â• â•©â•—â•‘â•£ ║║╚â•â•— + // ╚â•â•â•© ╩╚â•â•╚â•â•â•â•©â•╚â•â• + + + // â•”â•â•—â•”â•â•—â•”â•â•—â•”â•â•—â•”â•╗╦╔â•╗╔╦╗╦╔â•╗╔╗╔╔â•â•— + // â• â•╣╚â•╗╚â•â•—â•‘ â•‘â•‘ â•‘â• â•â•£ â•‘ â•‘â•‘ ║║║║╚â•â•— + // â•© ╩╚â•â•╚â•â•╚â•â•╚â•â•â•©â•© â•© â•© ╩╚â•â•â•╚â•╚â•â• + + }, + +}; + diff --git a/api/models/Transaction.js b/api/models/Transaction.js new file mode 100755 index 0000000..8dd8c6e --- /dev/null +++ b/api/models/Transaction.js @@ -0,0 +1,29 @@ +/** + * Transaction.js + * + * @description :: A model definition represents a database table/collection. + * @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models + */ + +module.exports = { + + attributes: { + + // â•”â•╗╦â•╗╦╔╦╗╦╔╦╗╦╦ ╦╔â•â•—â•”â•â•— + // â• â•â•╠╦â•â•‘â•‘â•‘â•‘â•‘ â•‘ ║╚╗╔â•â•‘â•£ ╚â•â•— + // â•© ╩╚â•â•©â•© â•©â•© â•© â•© ╚╠╚â•â•╚â•â• + + + // â•”â•╗╔╦╗╔╗ â•”â•╗╔╦╗╔â•â•— + // â•‘â•£ â•‘â•‘â•‘â• â•©â•—â•‘â•£ ║║╚â•â•— + // ╚â•â•â•© ╩╚â•â•╚â•â•â•â•©â•╚â•â• + + + // â•”â•â•—â•”â•â•—â•”â•â•—â•”â•â•—â•”â•╗╦╔â•╗╔╦╗╦╔â•╗╔╗╔╔â•â•— + // â• â•╣╚â•╗╚â•â•—â•‘ â•‘â•‘ â•‘â• â•â•£ â•‘ â•‘â•‘ ║║║║╚â•â•— + // â•© ╩╚â•â•╚â•â•╚â•â•╚â•â•â•©â•© â•© â•© ╩╚â•â•â•╚â•╚â•â• + + }, + +}; + diff --git a/config/custom.js b/config/custom.js index ee01994..7cc2791 100755 --- a/config/custom.js +++ b/config/custom.js @@ -23,7 +23,6 @@ module.exports.custom = { accountAddress : '0xDBF0C927F9E92dFE7C31e045e0Ba1067Ee205f73', contractABI : [{"constant":true,"inputs":[{"name":"ots","type":"string"}],"name":"getHash","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"ots","type":"string"}],"name":"getBlockNumber","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"ots","type":"string"},{"name":"file_hash","type":"string"}],"name":"verify","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"selfDestroy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"ots","type":"string"},{"name":"file_hash","type":"string"}],"name":"stamp","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"accountAddress"},{"indexed":true,"name":"hash","type":"string"},{"indexed":true,"name":"ots","type":"string"}],"name":"Stamped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"accountAddress"}],"name":"Deploy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"accountAddress"}],"name":"SelfDestroy","type":"event"}], contractAddress: '0xBD89a34041190439d43ec391486819eF5CBfBDBe', - privateKey: '6671485e4250881473c639465464148b5a0285461f136b585623333f22f7ca3f', - + privateKey: '6671485e4250881473c639465464148b5a0285461f136b585623333f22f7ca3f', }; diff --git a/config/routes.js b/config/routes.js index e0ed323..5fb752e 100755 --- a/config/routes.js +++ b/config/routes.js @@ -21,12 +21,11 @@ module.exports.routes = { '/': { view: 'pages/homepage' }, 'GET /blockchain/verify/:ots/:file_hash' : 'BlockchainController.verify', - 'GET /blockchain/get_block_number/:ots' : 'BlockchainController.getBlockNumber', - 'GET /blockchain/get_hash/:ots' : 'BlockchainController.getHash', - 'GET /blockchain/create_account' : 'BlockchainController.createAccount', + 'GET /account/create' : 'AccountController.create', + 'GET /account/get_balance/:account' : 'AccountController.getBalance', - 'POST /blockchain/send_transaction' : 'BlockchainController.sendTransaction', + 'POST /transaction/send' : 'TransactionController.send', 'POST /blockchain/stamp' : 'BlockchainController.stamp', diff --git a/package-lock.json b/package-lock.json index 060258d..640f4c5 100755 --- a/package-lock.json +++ b/package-lock.json @@ -2790,6 +2790,11 @@ "is-object": "^1.0.1" } }, + "js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + }, "js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -3319,6 +3324,11 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "nodejs-base64-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nodejs-base64-encode/-/nodejs-base64-encode-1.1.0.tgz", + "integrity": "sha512-KVAtRWpmL/Pb9X0eHb/w73Z9iVaiOKnEyXzijvp9Yopyf2iA97ciWq8YlXIgu2tDwUZ4JPswksbWixBNn1+LdQ==" + }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", diff --git a/package.json b/package.json index 4aed262..5bfb1c1 100755 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "@sailshq/socket.io-redis": "^5.2.0", "ethereumjs-tx": "^1.3.7", "grunt": "1.0.4", + "js-sha256": "^0.9.0", + "nodejs-base64-encode": "^1.1.0", "sails": "^1.2.3", "sails-hook-grunt": "^4.0.0", "sails-hook-orm": "^2.1.1", -- GitLab