mirror of
https://github.com/status-im/snt-gas-relay.git
synced 2025-01-27 14:44:47 +00:00
Adding gas-relayer source
This commit is contained in:
commit
7bae020fa8
31
app/gas-relayer/README.md
Normal file
31
app/gas-relayer/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# token-gas-relayer
|
||||
Gas Relayer implementation for Idea #73
|
||||
|
||||
|
||||
To execute as a daemon (only on POSIX systems)
|
||||
```
|
||||
bin/gas-relayer start
|
||||
bin/gas-relayer status
|
||||
bin/gas-relayer stop
|
||||
```
|
||||
|
||||
To execute js file directly
|
||||
```
|
||||
node src/service.js
|
||||
```
|
||||
|
||||
How to send a message to this service (all accounts and privatekeys should be replaced by your own test data)
|
||||
```
|
||||
shh.post({pubKey: PUBLIC_KEY, ttl: 1000, powTarget: 1, powTime: 20, topic: TOPIC_NAME, payload: PAYLOAD_BYTES});
|
||||
```
|
||||
- `PUBLIC_KEY` must contain the whisper public key used. It is shown on the console when running the service with `node`. With the provided configuration you can use the value:
|
||||
```
|
||||
0x044f1ee672354e54eab177ac4d5ce689d8b1f3d41dfc9778f494b99919c0cef9138f821611aecf84f1f2b972b5490d559e521a7a551f69c63e527ba29fbc06406a`
|
||||
```
|
||||
- `TOPIC_NAME` must contain one of the topic names generated based on converting the contract name to hex, and taking the first 8 bytes. For the provided configuration the following topics are available:
|
||||
- - IdentityGasRelay: `0x4964656e`
|
||||
- - SNTController: `0x534e5443`
|
||||
- `PAYLOAD_BYTES` a hex string that contains the identity/contract address to invoke and the web3 encoded abi function invocation plus parameters. If we were to execute `callGasRelayed(address,uint256,bytes,uint256,uint256,uint256,address,bytes)` in contract `0x692a70d2e424a56d2c6c27aa97d1a86395877b3a`, with these values: `"0x11223344556677889900998877665544332211",100,"0x00",1,10,20,"0x1122334455"` `PAYLOAD_BYTES` would contain the following hex string, where the first 20 bytes are the contract address, the next 4 bytes are the function signature (`0xfd0dded5`), and the rest of the values are the encoded parameters
|
||||
```
|
||||
0x692a70d2e424a56d2c6c27aa97d1a86395877b3afd0dded50000000000000000000000000011223344556677889900998877665544332211000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000011223344550000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000430783030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
```
|
272
app/gas-relayer/abi/ERC20.json
Normal file
272
app/gas-relayer/abi/ERC20.json
Normal file
@ -0,0 +1,272 @@
|
||||
[
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "name",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "approve",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "success",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "totalSupply",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_from",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transferFrom",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "success",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "decimals",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "version",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "balanceOf",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "balance",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "symbol",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transfer",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "success",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_value",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_extraData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "approveAndCall",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "success",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_spender",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "allowance",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "remaining",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_initialAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_tokenName",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "_decimalUnits",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"name": "_tokenSymbol",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"payable": false,
|
||||
"type": "fallback"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "_from",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "_to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "_value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Transfer",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "_owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "_spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "_value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Approval",
|
||||
"type": "event"
|
||||
}
|
||||
]
|
1
app/gas-relayer/abi/IdentityFactory.json
Normal file
1
app/gas-relayer/abi/IdentityFactory.json
Normal file
@ -0,0 +1 @@
|
||||
[{"constant":true,"inputs":[],"name":"latestKernel","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
|
1
app/gas-relayer/abi/IdentityGasRelay.json
Normal file
1
app/gas-relayer/abi/IdentityGasRelay.json
Normal file
@ -0,0 +1 @@
|
||||
[{"constant":false,"inputs":[{"name":"_baseToken","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"},{"name":"_data","type":"bytes"},{"name":"_nonce","type":"uint256"},{"name":"_gasPrice","type":"uint256"},{"name":"_gasMinimal","type":"uint256"},{"name":"_gasToken","type":"address"},{"name":"_messageSignatures","type":"bytes"}],"name":"approveAndCallGasRelayed","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"},{"name":"_data","type":"bytes"},{"name":"_nonce","type":"uint256"},{"name":"_gasPrice","type":"uint256"},{"name":"_gasMinimal","type":"uint256"},{"name":"_gasToken","type":"address"},{"name":"_messageSignatures","type":"bytes"}],"name":"callGasRelayed","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[],"name":"Debug","type":"event"}]
|
1
app/gas-relayer/abi/SNTController.json
Normal file
1
app/gas-relayer/abi/SNTController.json
Normal file
@ -0,0 +1 @@
|
||||
[{"constant":false,"inputs":[{"name":"a","type":"address"},{"name":"b","type":"bytes"},{"name":"c","type":"uint256"},{"name":"d","type":"uint256"},{"name":"e","type":"uint256"},{"name":"f","type":"bytes"}],"name":"executeGasRelayed","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"a","type":"address"},{"name":"b","type":"uint256"},{"name":"c","type":"uint256"},{"name":"d","type":"uint256"},{"name":"f","type":"bytes"}],"name":"transferSNT","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[],"name":"Debug","type":"event"}]
|
59
app/gas-relayer/config/config.json
Normal file
59
app/gas-relayer/config/config.json
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"blockchain": {
|
||||
"account": "0x9e14016ba37b23498885864053fded5226161a3a",
|
||||
"rpcHost": "localhost",
|
||||
"rpcPort": 8545
|
||||
},
|
||||
|
||||
"whisper": {
|
||||
"privateKey": "0x1d8ef80c9933e20fa9720bf82f3ad481c6be9e44920932c008bb76655a211add",
|
||||
"protocol": "ws",
|
||||
"host": "localhost",
|
||||
"port": 8546,
|
||||
"ttl": 20,
|
||||
"minPow": 0.8,
|
||||
"powTime": 1000
|
||||
},
|
||||
|
||||
"contracts":{
|
||||
"IdentityGasRelay": {
|
||||
"isIdentity": true,
|
||||
"factoryAddress": "0x24cb6a5b6c81e8ce3edb059ab97984c3d3ea2e3e",
|
||||
"abiFile": "../abi/IdentityGasRelay.json",
|
||||
"allowedFunctions": [
|
||||
{
|
||||
"function": "approveAndCallGasRelayed(address,address,uint256,bytes,uint256,uint256,uint256,address,bytes)",
|
||||
"to": "_to",
|
||||
"value": "_value",
|
||||
"data": "_data",
|
||||
"isToken": true,
|
||||
"token": "_baseToken"
|
||||
},
|
||||
{
|
||||
"function": "callGasRelayed(address,uint256,bytes,uint256,uint256,uint256,address,bytes)",
|
||||
"to": "_to",
|
||||
"value": "_value",
|
||||
"data": "_data",
|
||||
"isToken": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"SNTController": {
|
||||
"isIdentity": false,
|
||||
"address": "0x96f0811c6484c59c2674da1f64e725c01d82c1b5",
|
||||
"abiFile": "../abi/SNTController.json",
|
||||
"allowedFunctions": [
|
||||
{
|
||||
"function":"transferSNT(address,uint256,uint256,uint256,bytes)",
|
||||
"to": "_to",
|
||||
"value": "_amount"
|
||||
},
|
||||
{
|
||||
"function":"executeGasRelayed(address,bytes,uint256,uint256,uint256,bytes)",
|
||||
"to": "_to",
|
||||
"value": "_amount"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
16
app/gas-relayer/package.json
Normal file
16
app/gas-relayer/package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "gas-relayer",
|
||||
"version": "0.0.1",
|
||||
"description": "Gas relayer to avoid having to hold ether to perform transactions when you already have a token",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"daemonize2": "^0.4.2",
|
||||
"md5": "^2.2.1",
|
||||
"web3": "^1.0.0-beta.33"
|
||||
}
|
||||
}
|
207
app/gas-relayer/src/service.js
Normal file
207
app/gas-relayer/src/service.js
Normal file
@ -0,0 +1,207 @@
|
||||
const md5 = require('md5');
|
||||
const Web3 = require('web3');
|
||||
const config = require('../config/config.json');
|
||||
const web3 = new Web3(`${config.whisper.protocol}://${config.whisper.host}:${config.whisper.port}`);
|
||||
|
||||
const erc20ABI = require('../abi/ERC20.json');
|
||||
|
||||
console.info("Starting...")
|
||||
|
||||
async function start(){
|
||||
config.topics = [];
|
||||
for(let contractName in config.contracts){
|
||||
|
||||
// Obtaining the abis
|
||||
config.contracts[contractName].abi = require(config.contracts[contractName].abiFile);
|
||||
|
||||
const lngt = config.contracts[contractName].allowedFunctions.length;
|
||||
for(i = 0; i < lngt; i++){
|
||||
config.contracts[contractName].allowedFunctions[i].functionName = config.contracts[contractName].allowedFunctions[i].function.slice(0, config.contracts[contractName].allowedFunctions[i].function.indexOf('('));
|
||||
|
||||
// Extracting input
|
||||
config.contracts[contractName].allowedFunctions[i].inputs = config.contracts[contractName].abi.filter(x => x.name == config.contracts[contractName].allowedFunctions[i].functionName && x.type == "function")[0].inputs;
|
||||
|
||||
// Obtaining function signatures
|
||||
let functionSignature = web3.utils.sha3(config.contracts[contractName].allowedFunctions[i].function).slice(0, 10);
|
||||
config.contracts[contractName].allowedFunctions[functionSignature] = config.contracts[contractName].allowedFunctions[i];
|
||||
delete config.contracts[contractName].allowedFunctions[i];
|
||||
|
||||
}
|
||||
|
||||
config.contracts[contractName].functionSignatures = Object.keys(config.contracts[contractName].allowedFunctions);
|
||||
|
||||
// Extracting topics and available functions
|
||||
let topicName = web3.utils.toHex(contractName).slice(0, 10);
|
||||
config.topics.push(topicName);
|
||||
config.contracts[topicName] = config.contracts[contractName];
|
||||
config.contracts[topicName].name = contractName;
|
||||
delete config.contracts[contractName];
|
||||
|
||||
// Get Contract Bytecode
|
||||
let contractAddress = config.contracts[topicName].address;
|
||||
if(config.contracts[topicName].isIdentity){
|
||||
const lastKernelSignature = "0x4ac99424";
|
||||
let kernel = await web3.eth.call({to: config.contracts[topicName].factoryAddress, data: lastKernelSignature});
|
||||
contractAddress = '0x' + kernel.slice(26);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
config.contracts[topicName].code = md5(await web3.eth.getCode(contractAddress));
|
||||
} catch(err){
|
||||
console.error("Invalid contract for " + contractName);
|
||||
console.error(err);
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Setting up Whisper options
|
||||
const shhOptions = {
|
||||
ttl: config.whisper.ttl,
|
||||
minPow: config.whisper.minPow,
|
||||
};
|
||||
|
||||
let kId;
|
||||
|
||||
// Listening to whisper
|
||||
web3.shh.addPrivateKey(config.whisper.privateKey)
|
||||
.then((keyId) => {
|
||||
shhOptions.privateKeyID = keyId;
|
||||
kId = keyId;
|
||||
|
||||
web3.shh.getPublicKey(keyId).then(pk => {
|
||||
console.info(`Public Key: ${pk}`);
|
||||
console.info("Topics Available:");
|
||||
config.topics = [];
|
||||
for(let contractName in config.contracts) {
|
||||
console.info("- %s: %s [%s]", config.contracts[contractName].name, contractName, Object.keys(config.contracts[contractName].allowedFunctions).join(', '));
|
||||
}
|
||||
});
|
||||
|
||||
console.info("Started.");
|
||||
console.info("Listening for messages...")
|
||||
web3.shh.subscribe('messages', shhOptions, processMessages);
|
||||
});
|
||||
|
||||
|
||||
|
||||
const reply = async function(text, message){
|
||||
try {
|
||||
if(message.sig){
|
||||
let shhOptions = {
|
||||
pubKey: message.sig,
|
||||
sig: kId,
|
||||
ttl: config.whisper.ttl,
|
||||
powTarget:config.whisper.minPow,
|
||||
powTime: config.whisper.powTime,
|
||||
topic: message.topic,
|
||||
payload: web3.utils.fromAscii(text)
|
||||
};
|
||||
await web3.shh.post(shhOptions);
|
||||
}
|
||||
} catch(Err){
|
||||
// TODO
|
||||
console.error(Err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Process individual whisper message
|
||||
const processMessages = async function(error, message, subscription){
|
||||
if(error){
|
||||
// TODO log
|
||||
console.error(error);
|
||||
} else {
|
||||
const address = message.payload.slice(0, 42);
|
||||
const functionName = '0x' + message.payload.slice(42, 50);
|
||||
const functionParameters = '0x' + message.payload.slice(50);
|
||||
const payload = '0x' + message.payload.slice(42);
|
||||
|
||||
console.info("Processing request to: %s, %s", address, functionName);
|
||||
|
||||
if(!/^0x[0-9a-f]{40}$/i.test(address))
|
||||
return reply('Invalid address', message);
|
||||
|
||||
if(config.contracts[message.topic] == undefined)
|
||||
return reply('Invalid topic', message);
|
||||
|
||||
const contract = config.contracts[message.topic];
|
||||
if(!contract.functionSignatures.includes(functionName))
|
||||
return reply('Function not allowed', message) // TODO Log this
|
||||
|
||||
// Get code from address and compare it against the contract code
|
||||
const code = md5(await web3.eth.getCode(address));
|
||||
if(code != contract.code){
|
||||
return reply('Invalid contract code', message); // TODO Log this
|
||||
}
|
||||
|
||||
// Determining balances
|
||||
const params = web3.eth.abi.decodeParameters(contract.allowedFunctions[functionName].inputs, functionParameters);
|
||||
let balance;
|
||||
if(contract.isIdentity){
|
||||
if(contract.allowedFunctions[functionName].isToken){
|
||||
const Token = new web3.eth.Contract(erc20ABI, params[contracts.allowedFunctions[functionName].token]);
|
||||
balance = new web3.utils.BN(await Token.methods.balanceOf(address).call());
|
||||
} else {
|
||||
balance = new web3.utils.BN(await web3.eth.getBalance(address));
|
||||
}
|
||||
} else {
|
||||
// TODO SNT Controller
|
||||
}
|
||||
|
||||
// Estimating gas
|
||||
let estimatedGas = new web3.utils.BN(await web3.eth.estimateGas({
|
||||
to: address,
|
||||
data: params[contracts.allowedFunctions[functionName].data]
|
||||
}));
|
||||
|
||||
|
||||
// TODO determine if balance is enough
|
||||
|
||||
web3.eth.sendTransaction({
|
||||
from: config.blockchain.account,
|
||||
to: address,
|
||||
value: 0,
|
||||
data: payload
|
||||
})
|
||||
.then(function(receipt){
|
||||
return reply("Transaction mined;" + receipt.transactionHash, message);
|
||||
}).catch(function(err){
|
||||
reply("Couldn't mine transaction", message);
|
||||
// TODO log this?
|
||||
//console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
start();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Daemon helper functions
|
||||
|
||||
process.on("uncaughtException", function(err) {
|
||||
|
||||
});
|
||||
|
||||
process.on("SIGUSR1", function() {
|
||||
log("Reloading...");
|
||||
|
||||
|
||||
log("Reloaded.");
|
||||
});
|
||||
|
||||
process.once("SIGTERM", function() {
|
||||
log("Stopping...");
|
||||
});
|
||||
|
55
app/gas-relayer/test/sampleContracts.sol
Normal file
55
app/gas-relayer/test/sampleContracts.sol
Normal file
@ -0,0 +1,55 @@
|
||||
pragma solidity ^0.4.21;
|
||||
|
||||
contract TestIdentityGasRelay {
|
||||
event Debug();
|
||||
|
||||
function approveAndCallGasRelayed(
|
||||
address _baseToken,
|
||||
address _to,
|
||||
uint256 _value,
|
||||
bytes _data,
|
||||
uint _nonce,
|
||||
uint _gasPrice,
|
||||
uint _gasMinimal,
|
||||
address _gasToken,
|
||||
bytes _messageSignatures
|
||||
) external {
|
||||
emit Debug();
|
||||
}
|
||||
|
||||
function callGasRelayed(
|
||||
address _to,
|
||||
uint256 _value,
|
||||
bytes _data,
|
||||
uint _nonce,
|
||||
uint _gasPrice,
|
||||
uint _gasMinimal,
|
||||
address _gasToken,
|
||||
bytes _messageSignatures
|
||||
) external {
|
||||
emit Debug();
|
||||
}
|
||||
|
||||
function() payable {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
contract TestIdentityFactory {
|
||||
address public latestKernel;
|
||||
function TestIdentityFactory(){
|
||||
latestKernel = address(new TestIdentityGasRelay());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
contract TestSNTController {
|
||||
event Debug();
|
||||
function transferSNT(address a,uint256 b,uint256 c,uint256 d, bytes f){
|
||||
emit Debug();
|
||||
}
|
||||
function executeGasRelayed(address a,bytes b,uint256 c,uint256 d,uint256 e,bytes f){
|
||||
emit Debug();
|
||||
}
|
||||
}
|
100
app/gas-relayer/test/sendmsg.html
Normal file
100
app/gas-relayer/test/sendmsg.html
Normal file
@ -0,0 +1,100 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
||||
<title>Send whisper message</title>
|
||||
<style type="text/css">
|
||||
h4 small {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<body class="bg-light">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12 order-md-1">
|
||||
<h4 class="mb-3"><br />Send whisper message <small>ws://localhost:8546</small></h4>
|
||||
<p><code>web3</code> is available in your browser's console: <code>Tools > Developer Tools</code></p>
|
||||
<b>Keys</b>:
|
||||
<ul style="font-size:12px">
|
||||
<li>Public: <span class="pub"></span></li>
|
||||
<li>Private <span class="priv"></span></li>
|
||||
</ul>
|
||||
<form novalidate>
|
||||
<div class="mb-3">
|
||||
<label for="publicKey">Public Key</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">0x</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="publicKey" placeholder="Public Key" required>
|
||||
</div>
|
||||
<div class="invalid-feedback publicKey" style="width: 100%;">
|
||||
Invalid Public Key
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="topic">Topic</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">0x</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="topic" placeholder="" value="" required>
|
||||
</div>
|
||||
<div class="invalid-feedback topic" style="width: 100%;">
|
||||
Invalid Topic
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="ttl">TTL</label>
|
||||
<input type="text" class="form-control" id="ttl" value="1000" placeholder="" required>
|
||||
<div class="invalid-feedback ttl" style="width: 100%;">
|
||||
Invalid TTL
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="powTarget">PoW Target</label>
|
||||
<input type="text" class="form-control" id="powTarget" value="1" placeholder="" required>
|
||||
<div class="invalid-feedback powTarget" style="width: 100%;">
|
||||
Invalid PoW Target
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="powTime">PoW Time</label>
|
||||
<input type="text" class="form-control" id="powTime" value="20" placeholder="" required>
|
||||
<div class="invalid-feedback powTime" style="width: 100%;">
|
||||
Invalid PoW Time
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="payload">Payload</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">0x</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="payload" placeholder="Payload" required>
|
||||
</div>
|
||||
<div class="invalid-feedback payload" style="width: 100%;">
|
||||
Invalid Payload
|
||||
</div>
|
||||
</div>
|
||||
<hr class="mb-4">
|
||||
<p class="result"></p>
|
||||
<p id="messageArea"></p>
|
||||
<button class="btn btn-primary btn-lg btn-block" type="submit">Send Message</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
|
||||
<script src="web3.min.js"></script>
|
||||
<script src="sendmsg.js"></script>
|
||||
</body>
|
||||
</html>
|
103
app/gas-relayer/test/sendmsg.js
Normal file
103
app/gas-relayer/test/sendmsg.js
Normal file
@ -0,0 +1,103 @@
|
||||
$(function(){
|
||||
|
||||
const connUrl = "ws://localhost:8546";
|
||||
let web3 = new Web3(connUrl);
|
||||
window.web3 = web3;
|
||||
|
||||
let keyPair;
|
||||
|
||||
web3.shh.newKeyPair().then(async function(kid){
|
||||
$('.pub').text(await web3.shh.getPublicKey(kid));
|
||||
$('.priv').text(await web3.shh.getPrivateKey(kid));
|
||||
keyPair = kid;
|
||||
window.signature = kid;
|
||||
|
||||
web3.shh.subscribe('messages', {
|
||||
"privateKeyID": signature,
|
||||
"ttl": 20,
|
||||
"minPow": 0.8,
|
||||
"powTime": 1000
|
||||
}, function(error, message, subscription){
|
||||
console.log(web3.utils.hexToAscii(message.payload));
|
||||
$('#messageArea').text(web3.utils.hexToAscii(message.payload));
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
console.log("Connected to: %c%s", 'font-weight: bold', connUrl);
|
||||
|
||||
const add0x = function(elem){
|
||||
if(elem.val().slice(0, 2) != '0x'){
|
||||
return '0x' + elem.val();
|
||||
} else {
|
||||
let val = elem.val();
|
||||
elem.val(elem.val().slice(2));
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
$('button').on('click', async function(e){
|
||||
e.preventDefault();
|
||||
|
||||
let publicKey = add0x($("#publicKey"));
|
||||
let msgTopic = add0x($('#topic'));
|
||||
let msgPayload = add0x($('#payload'));
|
||||
let timeToLive = $('#ttl').val();
|
||||
let powTarget = $('#powTarget').val();
|
||||
let powTime = $('#powTime').val();
|
||||
|
||||
$('.invalid-feedback').hide();
|
||||
$('.is-invalid').removeClass('is-invalid');
|
||||
|
||||
if(!/^0x[0-9a-f]{130}$/i.test(publicKey)){
|
||||
$('#publicKey').addClass('is-invalid');
|
||||
$('.invalid-feedback.publicKey').show();
|
||||
}
|
||||
|
||||
if(!/^0x[0-9a-f]{8}$/i.test(msgTopic)){
|
||||
$('#topic').addClass('is-invalid');
|
||||
$('.invalid-feedback.topic').show();
|
||||
}
|
||||
|
||||
if(!/^0x[0-9a-f]+$/i.test(msgPayload) || msgPayload.length%2 > 0){
|
||||
$('#payload').addClass('is-invalid');
|
||||
$('.invalid-feedback.payload').show();
|
||||
}
|
||||
|
||||
if(!/^[0-9]+$/i.test(timeToLive)){
|
||||
$('#ttl').addClass('is-invalid');
|
||||
$('.invalid-feedback.ttl').show();
|
||||
}
|
||||
|
||||
if(!/^[+-]?([0-9]*[.])?[0-9]+$/.test(powTarget)){
|
||||
$('#powTarget').addClass('is-invalid');
|
||||
$('.invalid-feedback.powTarget').show();
|
||||
}
|
||||
|
||||
if(!/^[+-]?([0-9]*[.])?[0-9]+$/.test(powTime)){
|
||||
$('#powTime').addClass('is-invalid');
|
||||
$('.invalid-feedback.powTime').show();
|
||||
}
|
||||
|
||||
|
||||
if($('.is-invalid').length > 0) return;
|
||||
|
||||
console.log(`%c await web3.shh.post({pubKey: "${publicKey}", sig: signature, ttl: ${timeToLive}, powTarget: ${powTarget}, powTime: ${powTime}, topic: "${msgTopic}", payload: "${msgPayload}"})`, 'font-weight: bold');
|
||||
|
||||
let identity;
|
||||
|
||||
|
||||
web3.shh.post({ pubKey: publicKey,
|
||||
sig: keyPair,
|
||||
ttl: parseInt(timeToLive),
|
||||
powTarget: parseFloat(powTarget),
|
||||
powTime: parseFloat(powTime),
|
||||
topic: msgTopic,
|
||||
payload: msgPayload})
|
||||
.then(result => {
|
||||
console.log(result);
|
||||
$('p.result').html("<b>Response:</b> " + result);
|
||||
});
|
||||
});
|
||||
});
|
1
app/gas-relayer/test/web3.min.js
vendored
Normal file
1
app/gas-relayer/test/web3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user