140 lines
3.8 KiB
JavaScript
140 lines
3.8 KiB
JavaScript
const express = require('express');
|
|
const Web3 = require('web3');
|
|
const parseArgs = require('minimist');
|
|
const Account = require('./account.js');
|
|
const utils = require('./utils.js');
|
|
const fs = require('fs');
|
|
const morgan = require("morgan");
|
|
|
|
const argv = parseArgs(process.argv.slice(2), {string: ["sender", "bucket"], default: {"endpoint": "ws://127.0.0.1:8546"}});
|
|
const web3 = new Web3(argv["endpoint"]);
|
|
const account = new Account(web3);
|
|
|
|
const BucketConfig = utils.loadJSON(`./dist/contracts/Bucket.json`);
|
|
|
|
const port = process.env.PORT || 3000;
|
|
const app = express();
|
|
app.use(morgan('combined'))
|
|
|
|
let allowedBuckets = [];
|
|
|
|
async function redeem(bucket, message, sig) {
|
|
const Bucket = utils.json2Contract(web3, BucketConfig);
|
|
Bucket.transactionConfirmationBlocks = 1;
|
|
Bucket.options.address = bucket;
|
|
let methodCall = Bucket.methods.redeem(message, sig);
|
|
return account.sendMethod(methodCall, Bucket.options.address);
|
|
}
|
|
|
|
function validateAddress(addr) {
|
|
return addr != null && addr.startsWith("0x") && addr.length == 42;
|
|
}
|
|
|
|
function validate32Bytes(data) {
|
|
return data != null && data.startsWith("0x") && data.length == 66;
|
|
}
|
|
|
|
function validateNumber(num) {
|
|
return !isNaN(parseInt(num));
|
|
}
|
|
|
|
function validateBucket(bucket) {
|
|
return allowedBuckets.includes(bucket.toLowerCase());
|
|
}
|
|
|
|
function validateRequest(body) {
|
|
if (!validateAddress(body.bucket)) {
|
|
return "invalid bucket address";
|
|
} else if (!validateBucket(body.bucket)) {
|
|
return "cannot send to this bucket";
|
|
} else if (body.message === undefined) {
|
|
return "message must be specified";
|
|
} else if (!validateNumber(body.message.blockNumber)) {
|
|
return "invalid block number";
|
|
} else if (!validate32Bytes(body.message.blockHash)) {
|
|
return "invalid block hash";
|
|
} else if (!validateAddress(body.message.receiver)) {
|
|
return "invalid receiver address";
|
|
} else if (!validate32Bytes(body.message.code)) {
|
|
return "invalid code";
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function redeemOptions(req, res) {
|
|
res.append("Access-Control-Allow-Origin", ["*"]);
|
|
res.append("Access-Control-Allow-Headers", ["*"]);
|
|
res.json({message: "ok"})
|
|
}
|
|
|
|
async function redeemRequest(req, res) {
|
|
res.append("Access-Control-Allow-Origin", ["*"]);
|
|
res.append("Access-Control-Allow-Headers", ["*"]);
|
|
|
|
let err = validateRequest(req.body);
|
|
if (err) {
|
|
res.status(400).json({error: err});
|
|
}
|
|
|
|
try {
|
|
let receipt = await redeem(req.body.bucket, req.body.message, req.body.sig);
|
|
res.json({tx: receipt.transactionHash});
|
|
} catch(e) {
|
|
console.error(e)
|
|
res.status(500).json({error: "Couldn't send tx"});
|
|
}
|
|
}
|
|
|
|
function bucketRequest(req, res) {
|
|
if (validateBucket(req.params.address)) {
|
|
res.status(200).json({"allowed": true});
|
|
} else {
|
|
res.status(404).json({"allowed": false});
|
|
}
|
|
}
|
|
|
|
function loadBucketList(path) {
|
|
let file = fs.readFileSync(path, 'utf8');
|
|
allowedBuckets = file.split("\n").map((line) => line.toLowerCase().trim());
|
|
}
|
|
|
|
function checkBuckets() {
|
|
allowedBuckets = allowedBuckets.filter((line) => {
|
|
if (validateAddress(line)) {
|
|
return true;
|
|
} else {
|
|
console.warn(`${line} is an invalid bucket address, ignored`);
|
|
return false;
|
|
}
|
|
});
|
|
|
|
if (allowedBuckets.length == 0) {
|
|
console.error("no valid buckets, exiting");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
async function run() {
|
|
if (argv["bucket-list"]) {
|
|
loadBucketList(argv["bucket-list"]);
|
|
} else if (argv["bucket"]) {
|
|
allowedBuckets = [argv["bucket"].toLowerCase()];
|
|
} else {
|
|
console.error("the either the --bucket or --bucket-list option must be specified");
|
|
process.exit(1);
|
|
}
|
|
|
|
checkBuckets();
|
|
|
|
await account.init(argv);
|
|
|
|
app.use(express.json());
|
|
app.post('/redeem', redeemRequest);
|
|
app.options('/redeem', redeemOptions);
|
|
app.get('/bucket/:address', bucketRequest);
|
|
app.listen(port, () => console.log(`Relayer listening at http://localhost:${port}`));
|
|
}
|
|
|
|
run();
|