Documentation for Gas Relayer pt1

This commit is contained in:
Richard Ramos 2018-08-27 11:58:52 -04:00
parent 4dd1004c4e
commit 00a203896f
6 changed files with 93 additions and 4 deletions

View File

@ -1,5 +1,14 @@
/**
* Message Processor to analyze and execute strategies based on input objects
*/
class MessageProcessor { class MessageProcessor {
/**
* @param {object} config Configuration object obtained from `./config/config.js`
* @param {object} settings Settings obtained from parsing the configuration object
* @param {object} web3 Web3 object already configured
* @param {object} events Event emitter
*/
constructor(config, settings, web3, events){ constructor(config, settings, web3, events){
this.config = config; this.config = config;
this.settings = settings; this.settings = settings;
@ -7,6 +16,12 @@ class MessageProcessor {
this.events = events; this.events = events;
} }
/**
* Validate input message content
* @param {object} contract Contract object obtained from the settings based on the message topic
* @param {object} input Input object obtained from a message.
* @returns {object} State of validation
*/
async _validateInput(contract, input){ async _validateInput(contract, input){
console.info("Processing '%s' request to contract: %s", input.action, input.contract); console.info("Processing '%s' request to contract: %s", input.action, input.contract);
@ -37,6 +52,14 @@ class MessageProcessor {
return {success: true}; return {success: true};
} }
/**
* Process strategy and return validation result
* @param {object} contract Contract object obtained from the settings based on the message topic
* @param {object} input Input object obtained from a message.
* @param {function} reply Reply function to return message
* @param {object} strategy Strategy to apply. If undefined, it will use a strategy based on the contract
* @returns {object} State of validation
*/
async processStrategy(contract, input, reply, strategy){ async processStrategy(contract, input, reply, strategy){
const inputValidation = await this._validateInput(contract, input); const inputValidation = await this._validateInput(contract, input);
if(!inputValidation.success){ if(!inputValidation.success){
@ -62,6 +85,13 @@ class MessageProcessor {
} }
} }
/**
* Process strategy and based on its result, send a transaction to the blockchain
* @param {object} contract Contract object obtained from the settings based on the message topic
* @param {object} input Input object obtained from a message.
* @param {function} reply Reply function to return message
* @returns {undefined}
*/
async processTransaction(contract, input, reply){ async processTransaction(contract, input, reply){
const validationResult = await this.processStrategy(contract, input, reply); const validationResult = await this.processStrategy(contract, input, reply);
@ -89,7 +119,7 @@ class MessageProcessor {
try { try {
const receipt = await this.web3.eth.sendTransaction(p); const receipt = await this.web3.eth.sendTransaction(p);
// TODO: parse events // TODO: parse events
return reply("Transaction mined", receipt); reply("Transaction mined", receipt);
} catch(err){ } catch(err){
reply("Couldn't mine transaction: " + err.message); reply("Couldn't mine transaction: " + err.message);
// TODO log this? // TODO log this?

View File

@ -1,13 +1,22 @@
const Strategy = require('./BaseStrategy'); const Strategy = require('./BaseStrategy');
/**
* Class representing a strategy to validate an 'availability' request.
* @extends Strategy
*/
class AvailabilityStrategy extends Strategy { class AvailabilityStrategy extends Strategy {
async execute(input){ /**
* Process availability strategy
* @param {object} input Input object obtained from an 'availability' request. It expects an object with this structure `{contract, address, action, gasToken, gasPrice}`
* @returns {object} Status of validation, and minimum price
*/
execute(input){
// Verifying if token is allowed // Verifying if token is allowed
const token = this.settings.getToken(input.gasToken); const token = this.settings.getToken(input.gasToken);
if(token == undefined) return {success: false, message: "Token not allowed"}; if(token == undefined) return {success: false, message: "Token not allowed"};
// TODO Validate gasPrice, and return the minPrice accepted // TODO: Validate gasPrice, and return the minPrice accepted
const minPrice = 0.00; const minPrice = 0.00;
return { return {

View File

@ -2,7 +2,17 @@ const ganache = require("ganache-cli");
const Web3 = require('web3'); const Web3 = require('web3');
const erc20ABI = require('../../abi/ERC20Token.json'); const erc20ABI = require('../../abi/ERC20Token.json');
/**
* Abstract class used for validation strategies
*/
class BaseStrategy { class BaseStrategy {
/**
* @param {object} web3 Web3 object already configured
* @param {object} config Configuration object obtained from `./config/config.js`
* @param {object} settings Settings obtained from parsing the configuration object
* @param {object} contract Contract object obtained from the settings based on the message topic
*/
constructor(web3, config, settings, contract){ constructor(web3, config, settings, contract){
this.web3 = web3; this.web3 = web3;
this.settings = settings; this.settings = settings;
@ -10,6 +20,12 @@ class BaseStrategy {
this.config = config; this.config = config;
} }
/**
* Obtain the balance in tokens or ETH from an address
* @param {string} address ETH address to obtain the balance from
* @param {object} token Token obtained from `settings.getToken(tokenSymbol)`
* @returns {web3.utils.BN} Balance
*/
async getBalance(address, token){ async getBalance(address, token){
// Determining balances of token used // Determining balances of token used
if(token.symbol == "ETH"){ if(token.symbol == "ETH"){
@ -21,6 +37,11 @@ class BaseStrategy {
} }
} }
/**
* Build Parameters Function
* @param {object} input Input object obtained from an `transaction` request.
* @returns {function} Function that simplifies accessing contract functions' parameters
*/
_obtainParametersFunc(input){ _obtainParametersFunc(input){
const parameterList = this.web3.eth.abi.decodeParameters(this.contract.allowedFunctions[input.functionName].inputs, input.functionParameters); const parameterList = this.web3.eth.abi.decodeParameters(this.contract.allowedFunctions[input.functionName].inputs, input.functionParameters);
return function(parameterName){ return function(parameterName){
@ -28,6 +49,11 @@ class BaseStrategy {
}; };
} }
/**
* Estimate gas using web3
* @param {object} input Input object obtained from an `transaction` request.
* @returns {web3.utils.toBN} Estimated gas fees
*/
async _estimateGas(input){ async _estimateGas(input){
let p = { let p = {
from: this.config.node.blockchain.account, from: this.config.node.blockchain.account,
@ -40,6 +66,8 @@ class BaseStrategy {
/** /**
* Simulate transaction using ganache. Useful for obtaining events * Simulate transaction using ganache. Useful for obtaining events
* @param {object} input Input object obtained from an `transaction` request.
* @returns {object} Simulated transaction receipt
*/ */
async _simulateTransaction(input){ async _simulateTransaction(input){
let web3Sim = new Web3(ganache.provider({ let web3Sim = new Web3(ganache.provider({

View File

@ -1,8 +1,17 @@
const Strategy = require('./BaseStrategy'); const Strategy = require('./BaseStrategy');
const erc20ABI = require('../../abi/ERC20Token.json'); const erc20ABI = require('../../abi/ERC20Token.json');
/**
* Class representing a strategy to validate a `transaction` request when the topic is related to Identities.
* @extends Strategy
*/
class IdentityStrategy extends Strategy { class IdentityStrategy extends Strategy {
/**
* Validates if the contract being invoked represents an Identity instance
* @param {object} input Input object obtained from a `transaction` request.
* @returns {bool} Valid instance or not
*/
async _validateInstance(input){ async _validateInstance(input){
const instanceCodeHash = this.web3.utils.soliditySha3(await this.web3.eth.getCode(input.contract)); const instanceCodeHash = this.web3.utils.soliditySha3(await this.web3.eth.getCode(input.contract));
const kernelVerifSignature = this.web3.utils.soliditySha3(this.contract.kernelVerification).slice(0, 10); const kernelVerifSignature = this.web3.utils.soliditySha3(this.contract.kernelVerification).slice(0, 10);
@ -15,6 +24,11 @@ class IdentityStrategy extends Strategy {
return this.web3.eth.abi.decodeParameter('bool', verificationResult); return this.web3.eth.abi.decodeParameter('bool', verificationResult);
} }
/**
* Process Identity strategy
* @param {object} input Input object obtained from an 'transaction' request. It expects an object with this structure `{contract, address, action, functionName, functionParameters, payload}`
* @returns {object} Status of validation and estimated gas
*/
async execute(input){ async execute(input){
if(this.contract.isIdentity){ if(this.contract.isIdentity){
let validInstance = await this._validateInstance(input); let validInstance = await this._validateInstance(input);

View File

@ -3,9 +3,17 @@ const Strategy = require('./BaseStrategy');
const TransferSNT = "0x916b6511"; const TransferSNT = "0x916b6511";
const ExecuteGasRelayed = "0x754e6ab0"; const ExecuteGasRelayed = "0x754e6ab0";
/**
* Class representing a strategy to validate a `transaction` request when the topic is related to SNTController.
* @extends Strategy
*/
class SNTStrategy extends Strategy { class SNTStrategy extends Strategy {
/**
* Process SNTController strategy
* @param {object} input Input object obtained from an 'transaction' request. It expects an object with this structure `{contract, address, action, functionName, functionParameters, payload}`
* @returns {object} Status of validation and estimated gas
*/
async execute(input){ async execute(input){
const params = this._obtainParametersFunc(input); const params = this._obtainParametersFunc(input);