diff --git a/README.md b/README.md index 7b1cd01..93eea5f 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,9 @@ Before executing this program, `config/config.json` must be setup and `npm insta - Host, port and protocol Ganache will use when forking the blockchain for gas estimations and other operations - Wallet account used for processing the transactions - Symmetric key used to receive the Whisper messages -- Symmetric key used to send the heartbeats that notify the tokens and prices accepted - Accepted tokens information - Contract configuration + This program is configured with the default values for a embark installation run from 0 A `geth` node running whisper (via `-shh` option) is required. To execute the gas-relayer, you may use any of the following three methods. diff --git a/gas-relayer/src/contract-settings.js b/gas-relayer/src/contract-settings.js index 09bcad1..ec50aa4 100644 --- a/gas-relayer/src/contract-settings.js +++ b/gas-relayer/src/contract-settings.js @@ -105,8 +105,7 @@ class ContractSettings { // Obtaining strategy if(this.contracts[topicName].strategy){ - const strategy = require(this.contracts[topicName].strategy); - this.contracts[topicName].strategy = new strategy(this.web3, this.config, this, this.contracts[topicName]); + this.contracts[topicName].strategy = this.buildStrategy(this.contracts[topicName].strategy, topicName); } this._obtainContractBytecode(topicName); @@ -114,6 +113,11 @@ class ContractSettings { this._extractFunctions(topicName); } } + + buildStrategy(strategyFile, topicName){ + const strategy = require(strategyFile); + return new strategy(this.web3, this.config, this, this.contracts[topicName]); + } } diff --git a/gas-relayer/src/message-processor.js b/gas-relayer/src/message-processor.js index 1e03ae8..5dc073c 100644 --- a/gas-relayer/src/message-processor.js +++ b/gas-relayer/src/message-processor.js @@ -8,13 +8,13 @@ class MessageProcessor { } async _validateInput(contract, input){ - console.info("Processing request to: %s, %s", input.contract, input.functionName); + console.info("Processing '%s' request to contract: %s", input.action, input.contract); if(contract == undefined){ return {success: false, message: 'Unknown contract'}; } - if(!contract.functionSignatures.includes(input.functionName)){ + if(input.functionName && !contract.functionSignatures.includes(input.functionName)){ return {success: false, message: 'Function not allowed'}; } @@ -37,7 +37,7 @@ class MessageProcessor { return {success: true}; } - async process(contract, input, reply){ + async processStrategy(contract, input, reply, strategy){ const inputValidation = await this._validateInput(contract, input); if(!inputValidation.success){ // TODO Log? @@ -45,16 +45,26 @@ class MessageProcessor { return; } - let validationResult; + if(strategy || contract.strategy){ + let validationResult; + if(strategy){ + validationResult = await strategy.execute(input, reply); + } else { + validationResult = await contract.strategy.execute(input, reply); + } - if(contract.strategy){ - validationResult = await contract.strategy.execute(input, reply); if(!validationResult.success){ reply(validationResult.message); return; } - } + return validationResult; + } + } + + async processTransaction(contract, input, reply){ + const validationResult = await this.processStrategy(contract, input, reply); + let p = { from: this.config.node.blockchain.account, to: input.contract, diff --git a/gas-relayer/src/service.js b/gas-relayer/src/service.js index daf1147..ff521f9 100644 --- a/gas-relayer/src/service.js +++ b/gas-relayer/src/service.js @@ -4,9 +4,6 @@ const config = require('../config/config.js'); const ContractSettings = require('./contract-settings'); const MessageProcessor = require('./message-processor'); -// IDEA: A node should call an API (probably from a status node) to register itself as a -// token gas relayer. - console.info("Starting..."); const events = new EventEmitter(); @@ -84,7 +81,14 @@ events.on('setup:complete', async (settings) => { const replyFunction = (message) => (text, receipt) => { if(message.sig !== undefined){ - console.log(text); + + let payloadContent; + if(typeof text === 'object'){ + payloadContent = {...text, receipt}; + } else { + payloadContent = {text, receipt}; + } + web3.shh.post({ pubKey: message.sig, sig: shhOptions.kId, @@ -92,7 +96,7 @@ const replyFunction = (message) => (text, receipt) => { powTarget:config.node.whisper.minPow, powTime: config.node.whisper.powTime, topic: message.topic, - payload: web3.utils.fromAscii(JSON.stringify({message:text, receipt}, null, " ")) + payload: web3.utils.fromAscii(JSON.stringify(payloadContent, null, " ")) }).catch(console.error); } }; @@ -115,7 +119,7 @@ const extractInput = (message) => { obj.functionParameters = "0x" + parsedObj.encodedFunctionCall.slice(10); obj.payload = parsedObj.encodedFunctionCall; } else if(obj.action == 'availability') { - obj.token = parsedObj.token; + obj.gasToken = parsedObj.gasToken; obj.gasPrice = parsedObj.gasPrice; } } catch(err){ @@ -128,7 +132,7 @@ const extractInput = (message) => { events.on('server:listen', (shhOptions, settings) => { let processor = new MessageProcessor(config, settings, web3, events); - web3.shh.subscribe('messages', shhOptions, (error, message) => { + web3.shh.subscribe('messages', shhOptions, async (error, message) => { if(error){ console.error(error); return; @@ -138,15 +142,22 @@ events.on('server:listen', (shhOptions, settings) => { const input = extractInput(message); const reply = replyFunction(message); - + let validationResult; + switch(input.action){ case 'transaction': - processor.process(settings.getContractByTopic(message.topic), + processor.processTransaction(settings.getContractByTopic(message.topic), input, reply); break; case 'availability': - reply("available"); + validationResult = await processor.processStrategy(settings.getContractByTopic(message.topic), + input, + reply, + settings.buildStrategy("./strategy/AvailabilityStrategy", message.topic) + ); + if(validationResult.success) reply(validationResult.message); + break; default: reply("unknown-action"); diff --git a/gas-relayer/src/strategy/AvailabilityStrategy.js b/gas-relayer/src/strategy/AvailabilityStrategy.js new file mode 100644 index 0000000..447202e --- /dev/null +++ b/gas-relayer/src/strategy/AvailabilityStrategy.js @@ -0,0 +1,24 @@ +const Strategy = require('./BaseStrategy'); + +class AvailabilityStrategy extends Strategy { + + async execute(input){ + // Verifying if token is allowed + const token = this.settings.getToken(input.gasToken); + if(token == undefined) return {success: false, message: "Token not allowed"}; + + // TODO Validate gasPrice, and return the minPrice accepted + const minPrice = 0.00; + + return { + success: true, + message: { + message: "Available", + minPrice: minPrice + } + }; + } + +} + +module.exports = AvailabilityStrategy; diff --git a/test-dapp/app/components/body-identity.js b/test-dapp/app/components/body-identity.js index d454179..710ba1c 100644 --- a/test-dapp/app/components/body-identity.js +++ b/test-dapp/app/components/body-identity.js @@ -57,7 +57,7 @@ class Body extends Component { const msg = web3js.utils.toAscii(message.payload); const msgObj = JSON.parse(msg); - if(msgObj.message == 'available'){ + if(msgObj.message == 'Available'){ // found a relayer console.log("Relayer available: " + message.sig); diff --git a/test-dapp/app/components/body-sntcontroller.js b/test-dapp/app/components/body-sntcontroller.js index 22352f1..28c1220 100644 --- a/test-dapp/app/components/body-sntcontroller.js +++ b/test-dapp/app/components/body-sntcontroller.js @@ -59,7 +59,7 @@ class Body extends Component { const msg = web3js.utils.toAscii(message.payload); const msgObj = JSON.parse(msg); - if(msgObj.message == 'available'){ + if(msgObj.message == 'Available'){ // found a relayer console.log("Relayer available: " + message.sig);