64 Commits

Author SHA1 Message Date
52473197ea Add protection from abusing relayers (force spending fee for reverted tx): fail if address is invalid or smart-contract (not EOA) 2023-10-16 16:40:03 -07:00
cb1212d793 Resolve all Tornado dependencies via self-hosted registry on Gitea 2023-09-18 08:54:05 -07:00
531567d8b2 Update tornado-oracles & calculate and check relayer & user-provided fee correctly 2023-09-11 20:22:20 -07:00
67c0782794 Calculate withdrawal gas & token prices via @tornado/tornado-oracles 2023-09-02 10:39:27 -07:00
40c55b3e7c Add build command, that docker container will be build automatically by installation script in main v5 repo 2023-07-25 08:52:45 -07:00
fd72f09e11 Fix .env to deploy with docker-compose & fix redis for local deployment 2023-07-24 07:34:48 -07:00
fdbbb05733 Bump relayer version to 5.1.0 2023-07-16 14:19:48 -07:00
eb908ff1f5 Bump node & move installation script to main-v5 branch 2023-07-16 14:11:44 -07:00
44f70bd41d Add v5 correct gas price/gas limit estimation 2023-07-16 14:10:48 -07:00
16a17079eb Run locally with concurrently to be Windows-compatible & use circomlib from our registry 2023-07-14 07:53:32 -07:00
6adeb27b83 Change .env name for mainnet 2023-07-11 19:17:20 -07:00
58dae5b030 Edit docker-compose config: change docker images to prebuilt local, change .env files and profiles naming for sidechains 2023-06-10 19:16:11 +03:00
3b13d3f508 Create .env for ETH mainnet 2023-06-10 19:12:24 +03:00
6b1a41585b Update tornado cash site link on relayer site index page 2023-05-06 14:00:32 +03:00
e90d1c4086 Create script to autoinstall dependecies & configure nginx & build docker containers for debian-based distros 2023-05-06 13:57:38 +03:00
61f464441b Delete old docker-compose.yml 2023-05-06 13:55:47 +03:00
gozzy
ed5d99cf44 nginx template ddos mitigation 2023-03-26 21:50:17 +00:00
gozzy
7d10fe2ab9 decrease minimumBalance & disable treeWatcher by default 2023-03-26 11:21:24 +00:00
gozzy
7d3cb5be49 update default rpc 2023-03-20 01:09:46 +00:00
gozzy
ff05e30bd2 update reverse-proxy app port 2023-03-19 21:18:15 +00:00
gozzy
1e8ddffdf1 update README.md 2023-03-19 21:13:12 +00:00
gozzy
3dc9314e29 local image compilation 2023-03-19 19:43:40 +00:00
gozzy
5f3da2578a yaml multi-network config 2023-03-18 23:01:06 +00:00
gozzy
cd6bd25d2c reinstate #1 2023-03-14 16:33:51 +00:00
smart_ex
2247730603 bump tx-manager 2022-05-25 19:21:03 +10:00
smart_ex
c915c97b85 priceWatcher oracle fix 2022-05-25 19:21:03 +10:00
smart_ex
582af773e6 tornado proxy address 2022-05-25 19:21:03 +10:00
smart_ex
9488090892 fix logging error messages 2022-05-25 19:21:03 +10:00
smart_ex
632dce129d fix queue collision 2022-05-25 19:21:03 +10:00
smart_ex
76cda01ee1 validate only selected network contract addresses 2022-05-25 19:21:03 +10:00
smart_ex
7f657c1d7d add test for isKnownContract validation 2022-05-25 19:21:03 +10:00
smart_ex
e386a1d23c fix hash map key 2022-05-25 19:21:03 +10:00
smart_ex
16d8e0fc28 fix contract address validation 2022-05-25 19:21:03 +10:00
smart_ex
a7fc9c4b24 block iframe 2022-05-25 19:21:03 +10:00
smart_ex
ee9e27ecad move errorsLog to health 2022-05-25 19:21:03 +10:00
smart_ex
95c6dc23c6 increment errors score 2022-05-25 19:21:03 +10:00
smart_ex
cfcf1c8677 show errors on status page 2022-05-25 19:21:03 +10:00
smart_ex
8868040882 write errors with positive score 2022-05-25 19:21:03 +10:00
smart_ex
50054e0516 bit of refactor, add RelayerError class 2022-05-25 19:21:03 +10:00
smart_ex
76209e11c0 remove proposal 10 check 2022-05-25 19:21:03 +10:00
Danil Kovtonyuk
3c5eaa2c4b Update README.md 2022-04-13 21:11:16 +10:00
_den
fd36dd5c5e added link to instructions for running rpc nodes 2022-03-10 21:11:06 +10:00
Alexey Pertsev
350d1f1d11 Update Readme 2022-03-01 11:32:30 +01:00
Danil Kovtonyuk
49a90872a2 fix: gas limit 2022-02-22 04:33:26 +10:00
Sergei SMART
2f79125dd1 init check, add Governance contract 2022-02-16 11:19:40 +01:00
_den
1f900843de added discord notification (#86)
* added discord notification

* updated notification message

* updated discord notification

* updated discord notification message

Co-authored-by: _den <_den@outlook.com>
2022-02-16 10:37:24 +01:00
dependabot[bot]
cc5ec13d97 Bump simple-get from 2.8.1 to 2.8.2
Bumps [simple-get](https://github.com/feross/simple-get) from 2.8.1 to 2.8.2.
- [Release notes](https://github.com/feross/simple-get/releases)
- [Commits](https://github.com/feross/simple-get/compare/v2.8.1...v2.8.2)

---
updated-dependencies:
- dependency-name: simple-get
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-16 10:34:03 +01:00
dependabot[bot]
9e8a6c79ac Bump node-fetch from 2.6.1 to 2.6.7
Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 2.6.1 to 2.6.7.
- [Release notes](https://github.com/node-fetch/node-fetch/releases)
- [Commits](https://github.com/node-fetch/node-fetch/compare/v2.6.1...v2.6.7)

---
updated-dependencies:
- dependency-name: node-fetch
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-16 10:33:45 +01:00
dependabot[bot]
38b1169eae Bump pathval from 1.1.0 to 1.1.1
Bumps [pathval](https://github.com/chaijs/pathval) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/chaijs/pathval/releases)
- [Changelog](https://github.com/chaijs/pathval/blob/master/CHANGELOG.md)
- [Commits](https://github.com/chaijs/pathval/compare/v1.1.0...v1.1.1)

---
updated-dependencies:
- dependency-name: pathval
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-16 10:32:56 +01:00
dependabot[bot]
d95d495853 Bump tar from 4.4.13 to 4.4.19
Bumps [tar](https://github.com/npm/node-tar) from 4.4.13 to 4.4.19.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v4.4.13...v4.4.19)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-16 10:32:45 +01:00
dependabot[bot]
54e1c03f9e Bump glob-parent from 5.1.1 to 5.1.2
Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/gulpjs/glob-parent/releases)
- [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: glob-parent
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-16 10:32:33 +01:00
dependabot[bot]
429723f370 Bump normalize-url from 4.5.0 to 4.5.1
Bumps [normalize-url](https://github.com/sindresorhus/normalize-url) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/sindresorhus/normalize-url/releases)
- [Commits](https://github.com/sindresorhus/normalize-url/commits)

---
updated-dependencies:
- dependency-name: normalize-url
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-16 10:32:18 +01:00
dependabot[bot]
b457820cf5 Bump lodash from 4.17.20 to 4.17.21
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-16 10:31:27 +01:00
Danil Kovtonyuk
31d697701e fix: lint 2022-01-19 22:11:00 +10:00
_den
b103033103 added zabbix guide to README.md 2022-01-19 22:03:39 +10:00
_den
1bb2d5f044 updated installation instructions 2022-01-19 22:03:39 +10:00
_den
44f1e1ec7a added templates for monitoring with zabbix 2022-01-19 22:03:39 +10:00
Danil Kovtonyuk
043356f5fe fix: initializing mining tree 2021-10-09 03:15:56 +10:00
Danil Kovtonyuk
8f5b673f3b feat: add env baseFeeReserve variable 2021-09-08 23:15:25 +10:00
Danil Kovtonyuk
82e5f4ee70 feat: add EIP-1559 support 2021-09-08 23:15:25 +10:00
nikdementev
cc0a252040 fix: bump cdai gas limit 2021-07-30 20:39:03 +03:00
nikdementev
a46afed752 styles: prettier 2021-06-19 23:20:19 +03:00
nikdementev
0cd9e515ae fix: price method 2021-06-19 23:10:31 +03:00
nikdementev
fa77997896 fix: prices oracle 2021-06-19 23:05:38 +03:00
43 changed files with 96055 additions and 2879 deletions

View File

@@ -1,8 +1,8 @@
NET_ID=1
HTTP_RPC_URL=https://mainnet.infura.io
HTTP_RPC_URL=https://api.securerpc.com/v1
WS_RPC_URL=wss://mainnet.infura.io/ws/v3/
# ORACLE_RPC_URL should always point to the mainnet
ORACLE_RPC_URL=https://mainnet.infura.io
ORACLE_RPC_URL=https://api.securerpc.com/v1
REDIS_URL=redis://127.0.0.1:6379
# DNS settings
@@ -13,11 +13,12 @@ APP_PORT=8000
# without 0x prefix
PRIVATE_KEY=
# 0.05 means 0.05%
REGULAR_TORNADO_WITHDRAW_FEE=0.05
RELAYER_FEE=0.4
MINING_SERVICE_FEE=0.05
REWARD_ACCOUNT=
CONFIRMATIONS=4
# in GWEI
MAX_GAS_PRICE=1000
AGGREGATOR=0x8cb1436F64a3c33aD17bb42F94e255c4c0E871b2
BASE_FEE_RESERVE_PERCENTAGE=25
AGGREGATOR=0xE8F47A78A6D52D317D0D2FFFac56739fE14D1b49

View File

@@ -19,7 +19,7 @@ jobs:
- run: yarn test
- run: yarn lint
- name: Telegram Failure Notification
uses: appleboy/telegram-action@0.0.7
uses: appleboy/telegram-action@master
if: failure()
with:
message: ❗ Build failed for [${{ github.repository }}](https://github.com/${{ github.repository }}/actions) because of ${{ github.actor }}
@@ -56,7 +56,7 @@ jobs:
password: ${{ secrets.DOCKER_TOKEN }}
- name: Telegram Message Notify
uses: appleboy/telegram-action@0.0.7
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_CHAT_ID }}
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
@@ -65,22 +65,35 @@ jobs:
format: markdown
- name: Telegram Relayer Channel Notification
uses: appleboy/telegram-action@0.0.7
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_RELAYER_CHAT_ID }}
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
message: |
🚀 Published a new version of the relayer node service to docker hub: `tornadocash/relayer:v${{ steps.vars.outputs.version }}` and `tornadocash/relayer:mining`.
🚀 Published a new version of the relayer node service for mainnet to docker hub: `tornadocash/relayer:v${{ steps.vars.outputs.version }}` and `tornadocash/relayer:mining`.
Please update your mainnet nodes ❗️
DO NOT TOUCH SIDECHAINS AND NOVA RELAYERS.
Please update your nodes ❗️
debug: true
format: markdown
- name: Discord Relayer Channel Notification
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_RELAYER_WEBHOOK }}
uses: Ilshidur/action-discord@master
with:
args: |
🚀 Published a new version of the relayer node service for mainnet to docker hub: `tornadocash/relayer:v${{ steps.vars.outputs.version }}` and `tornadocash/relayer:mining`.
Please update your mainnet nodes ❗️
DO NOT TOUCH SIDECHAINS AND NOVA RELAYERS.
- name: Telegram Failure Notification
uses: appleboy/telegram-action@0.0.7
uses: appleboy/telegram-action@master
if: failure()
with:
message: ❗ Failed to publish [${{ steps.vars.outputs.repo_name }}](https://github.com/${{ github.repository }}/actions) because of ${{ github.actor }}
message: ❗ Failed to publish [${{ steps.vars.outputs.repo_name }}](https://github.com/${{ github.repository }}/actions):v${{ steps.vars.outputs.version }} for mainnet because of ${{ github.actor }}
format: markdown
to: ${{ secrets.TELEGRAM_CHAT_ID }}
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
@tornado:registry=https://git.tornado.ws/api/packages/tornado-packages/npm/

View File

@@ -1,4 +1,4 @@
FROM node:12
FROM node:16
WORKDIR /app
COPY package.json yarn.lock ./

131
README.md
View File

@@ -1,83 +1,100 @@
# Relayer for Tornado Cash [![Build Status](https://github.com/tornadocash/relayer/workflows/build/badge.svg)](https://github.com/tornadocash/relayer/actions) [![Docker Image Version (latest semver)](https://img.shields.io/docker/v/tornadocash/relayer?logo=docker&logoColor=%23FFFFFF&sort=semver)](https://hub.docker.com/repository/docker/tornadocash/relayer)
# Relayer for Tornado Cash [![Build Status](https://github.com/tornadocash/relayer/workflows/build/badge.svg)](https://github.com/tornadocash/relayer/actions) ![Static Badge](https://img.shields.io/badge/version-5.1.0-blue?logo=docker)
## Getting listed on app.tornado.cash
__*Tornado Cash was sanctioned by the US Treasury on 08/08/2022, this makes it illegal for US citizens to interact with Tornado Cash and all of it's associated deployed smart contracts. Please understand the laws where you live and take all necessary steps to protect and anonymize yourself.__
If you would like to be listed in tornado.cash UI relayer's dropdown option, please do the following:
__*It is recommended to run your Relayer on a VPS instance ([Virtual Private Server](https://njal.la/)). Ensure SSH configuration is enabled for security, you can find information about SSH keygen and management [here](https://www.ssh.com/academy/ssh/keygen).__
1. Setup tornado.cash relayer node(see below for docker-compose.yml example)
2. Setup ENS subdomain(`goerli-v2.xxx.eth`, `mainnet-v2.xxx.eth`) with TEXT record and URL key that points to your DNS or IP address.
3. Test your relayer setup on Goerli testnet at https://app.tornado.cash by choosing custom relayer's option on withdraw tab. Enter your ens name and initiate a withdrawal.
4. Open new Github issue in https://github.com/tornadocash/tornado-relayer/issues and specify the following:
## Deploy with docker-compose (recommended)
- your goerli ens url
- your mainnet ens url
- your telegram handle
- withdrawal tx on goerli
- withdrawal tx on mainnet
*The following instructions are for Ubuntu 22.10, other operating systems may vary. These instructions include automated SSL configuration with LetsEncrypt.*
Please choose your testnet relayer's fee wisely.
__PREREQUISITES__
1. Update core dependencies
- `sudo apt-get update`
2. Install docker-compose
- `curl -SL https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose && sudo chmod +x /usr/local/bin/docker-compose`
3. Install Docker
- `curl -fsSL https://get.docker.com -o get-docker.sh && chmod +x get-docker.sh && ./get-docker.sh`
4. Install git
- `sudo apt-get install git-all`
5. Install nginx
- `sudo apt install nginx`
6. Stop apache2 instance (enabled by default)
- `sudo systemctl stop apache2`
Disclaimer: Please consult with legal and tax advisors regarding the compliance of running a relayer service in your jurisdiction. The authors of this project bear no responsibility.
__FIREWALL CONFIGURATION__
USE AT YOUR OWN RISK.
_* Warning: Failure to configure SSH as the first UFW rule, will lock you out of the instance_
## Deploy with docker-compose
1. Make sure UFW is installed by running `apt update` and `apt install ufw`
2. Allow SSH in the first position in UFW by running `ufw insert 1 allow ssh`*
3. Allow HTTP, and HTTPS by running `ufw allow https/tcp/http`
4. Finalize changes and enable firewall `ufw enable`
docker-compose.yml contains a stack that will automatically provision SSL certificates for your domain name and will add a https redirect to port 80.
__DEPLOYMENT__
1. Clone the repository and enter the directory
- `git clone https://git.tornado.ws/tornadocash/classic-relayer -b mainnet-v5 && cd classic-relayer`
2. Clone the example environment file `.env.example` to configure for the preferred network - `cp .env.example .env` , then fill `.env` file.
- Set `PRIVATE_KEY` for your relayer address (remove the 0x from your private key)
- Set `VIRTUAL_HOST` and `LETSENCRYPT_HOST` to your domain address
- add a A record DNS record with the value assigned to your instance IP address to configure the domain
- Set `RELAYER_FEE` to what you would like to charge as your fee (remember 0.3% is deducted from your staked relayer balance)
- Set `RPC_URL` to a non-censoring RPC endpoint (You can [run your own](https://github.com/feshchenkod/rpc-nodes), or use a [free option](https://chainnodes.org/))
- Set `ORACLE_RPC_URL` to an Ethereum native RPC endpoint
1. Download [docker-compose.yml](/docker-compose.yml) and [.env.example](/.env.example)
4. Uncomment the `env_file` lines (remove `# `) for the associated network services in `docker-compose.yml`
5. Build and deploy the docker source by specifying the network through:
```
wget https://raw.githubusercontent.com/tornadocash/tornado-relayer/master/docker-compose.yml
wget https://raw.githubusercontent.com/tornadocash/tornado-relayer/master/.env.example -O .env
```
- `npm run build`
- `docker-compose up -d`
5. Visit your domain address and check the `/status` endpoint and ensure there is no errors in the `status` field
2. Setup environment variables
- set `NET_ID` (1 for mainnet, 5 for Goerli)
- set `HTTP_RPC_URL` rpc url for your ethereum node
- set `WS_RPC_URL` websocket url
- set `ORACLE_RPC_URL` - rpc url for mainnet node for fetching prices(always have to be on mainnet)
- set `PRIVATE_KEY` for your relayer address (without 0x prefix)
- set `VIRTUAL_HOST` and `LETSENCRYPT_HOST` to your domain and add DNS record pointing to your relayer ip address
- set `REGULAR_TORNADO_WITHDRAW_FEE` - fee in % that is used for tornado pool withdrawals
- set `MINING_SERVICE_FEE` - fee in % that is used for mining AP withdrawals
- set `REWARD_ACCOUNT` - eth address that is used to collect fees
- update `AGGREGATOR` if needed - Contract address of aggregator instance.
- update `CONFIRMATIONS` if needed - how many block confirmations to wait before processing an event. Not recommended to set less than 3
- update `MAX_GAS_PRICE` if needed - maximum value of gwei value for relayer's transaction
If you want to use more than 1 eth address for relaying transactions, please add as many `workers` as you want. For example, you can comment out `worker2` in docker-compose.yml file, but please use a different `PRIVATE_KEY` for each worker.
3. Run `docker-compose up -d`
__NGINX REVERSE PROXY__
1. Copy the pre-modified nginx policy as your default policy
- `cp tornado.conf /etc/nginx/sites-available/default`
2. Append the default nginx configuration to include streams
- `echo "stream { map_hash_bucket_size 128; map_hash_max_size 128; include /etc/nginx/conf.d/streams/*.conf; }" >> /etc/nginx/nginx.conf`
3. Create the stream configuration
- `mkdir /etc/nginx/conf.d/streams && cp tornado-stream.conf /etc/nginx/conf.d/streams/tornado-stream.conf`
4. Start nginx to make sure the configuration is correct
- `sudo systemctl restart nginx`
5. Stop nginx
- `sudo systemctl stop nginx`
## Run locally
1. `npm i`
2. `cp .env.example .env`
3. Modify `.env` as needed
4. `npm run start`
5. Go to `http://127.0.0.1:8000`
6. In order to execute withdraw request, you can run following command
1. `npm i -g yarn` (if yarn not installed in your system)
2. `yarn`
3. `cp .env.mainnet.example .env`
4. Modify `.env` as needed
5. `yarn start`
6. Go to `http://127.0.0.1:8000`
7. In order to execute withdraw request, you can run following command
```bash
curl -X POST -H 'content-type:application/json' --data '<input data>' http://127.0.0.1:8000/relay
```
Relayer should return a transaction hash
Relayer should return a transaction hash.
In that case you will need to add https termination yourself because browsers with default settings will prevent https
tornado.cash UI from submitting your request over http connection
_Note._ If you want to change contracts' addresses go to [config.js](./config.js) file.
## Architecture
## Input data example
1. TreeWatcher module keeps track of Account Tree changes and automatically caches the actual state in Redis and emits `treeUpdate` event to redis pub/sub channel
2. Server module is Express.js instance that accepts http requests
3. Controller contains handlers for the Server endpoints. It validates input data and adds a Job to Queue.
4. Queue module is used by Controller to put and get Job from queue (bull wrapper)
5. Status module contains handler to get a Job status. It's used by UI for pull updates
6. Validate contains validation logic for all endpoints
7. Worker is the main module that gets a Job from queue and processes it
```json
{
"proof": "0x0f8cb4c2ca9cbb23a5f21475773e19e39d3470436d7296f25c8730d19d88fcef2986ec694ad094f4c5fff79a4e5043bd553df20b23108bc023ec3670718143c20cc49c6d9798e1ae831fd32a878b96ff8897728f9b7963f0d5a4b5574426ac6203b2456d360b8e825d8f5731970bf1fc1b95b9713e3b24203667ecdd5939c2e40dec48f9e51d9cc8dc2f7f3916f0e9e31519c7df2bea8c51a195eb0f57beea4924cb846deaa78cdcbe361a6c310638af6f6157317bc27d74746bfaa2e1f8d2e9088fd10fa62100740874cdffdd6feb15c95c5a303f6bc226d5e51619c5b825471a17ddfeb05b250c0802261f7d05cf29a39a72c13e200e5bc721b0e4c50d55e6",
"args": [
"0x1579d41e5290ab5bcec9a7df16705e49b5c0b869095299196c19c5e14462c9e3",
"0x0cf7f49c5b35c48b9e1d43713e0b46a75977e3d10521e9ac1e4c3cd5e3da1c5d",
"0x03ebd0748aa4d1457cf479cce56309641e0a98f5",
"0xbd4369dc854c5d5b79fe25492e3a3cfcb5d02da5",
"0x000000000000000000000000000000000000000000000000058d15e176280000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
"contract": "0xA27E34Ad97F171846bAf21399c370c9CE6129e0D"
}
```
Disclaimer:

View File

@@ -1,12 +0,0 @@
[
{
"inputs": [
{ "internalType": "contract IERC20", "name": "srcToken", "type": "address" },
{ "internalType": "contract IERC20", "name": "dstToken", "type": "address" }
],
"name": "getRate",
"outputs": [{ "internalType": "uint256", "name": "weightedRate", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
}
]

2
app.js
View File

@@ -1 +1 @@
module.exports = require('./src/index')
module.exports = require('./src/server')

85902
cache/accounts_farmer_1.json vendored Normal file

File diff suppressed because it is too large Load Diff

5878
cache/accounts_farmer_5.json vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,62 +0,0 @@
version: '2'
# ssh-agent && ssh-add -K ~/.ssh/id_rsa
# DOCKER_BUILDKIT=1 docker build --ssh default -t tornadocash/relayer .
services:
server:
image: tornadocash/relayer
restart: always
command: server
env_file: .env
ports:
- 8000:8000
environment:
REDIS_URL: redis://redis/0
nginx_proxy_read_timeout: 600
depends_on: [redis]
treeWatcher:
image: tornadocash/relayer
restart: always
command: treeWatcher
env_file: .env
environment:
REDIS_URL: redis://redis/0
depends_on: [redis]
priceWatcher:
image: tornadocash/relayer
restart: always
command: priceWatcher
env_file: .env
environment:
REDIS_URL: redis://redis/0
depends_on: [redis]
worker1:
image: tornadocash/relayer
restart: always
command: worker
env_file: .env
environment:
REDIS_URL: redis://redis/0
depends_on: [redis]
# worker2:
# image: tornadocash/relayer
# restart: always
# command: worker
# env_file: .env
# environment:
# PRIVATE_KEY: qwe
# REDIS_URL: redis://redis/0
redis:
image: redis
restart: always
command: [redis-server, --appendonly, 'yes']
volumes:
- redis:/data
volumes:
redis:

View File

@@ -1,59 +1,118 @@
version: '2'
services:
server:
image: tornadocash/relayer:mining
redis:
image: redis
restart: always
command: [redis-server, --appendonly, 'yes']
volumes:
- redis:/data
ports:
- '127.0.0.1:6379:6379'
nginx:
image: nginx:alpine
container_name: nginx
restart: always
ports:
- 80:80
- 443:443
volumes:
- conf:/etc/nginx/conf.d
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
- certs:/etc/nginx/certs
logging:
driver: none
dockergen:
image: poma/docker-gen
container_name: dockergen
restart: always
command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
volumes_from:
- nginx
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
container_name: letsencrypt
restart: always
environment:
NGINX_DOCKER_GEN_CONTAINER: dockergen
volumes_from:
- nginx
- dockergen
# ---------------------- ETH Mainnet ----------------------- #
eth-server:
build: .
image: tornadorelayer:mainnet
profiles: ['eth']
restart: always
command: server
env_file: .env
env_file: .env.eth
environment:
NET_ID: 1
REDIS_URL: redis://redis/0
nginx_proxy_read_timeout: 600
depends_on: [redis]
treeWatcher:
image: tornadocash/relayer:mining
eth-treeWatcher:
image: tornadorelayer:mainnet
profiles: ['eth']
restart: always
command: treeWatcher
env_file: .env
env_file: .env.eth
environment:
NET_ID: 1
REDIS_URL: redis://redis/0
depends_on: [redis]
depends_on: [redis, eth-server]
priceWatcher:
image: tornadocash/relayer:mining
eth-priceWatcher:
image: tornadorelayer:mainnet
profiles: ['eth']
restart: always
command: priceWatcher
env_file: .env
env_file: .env.eth
environment:
NET_ID: 1
REDIS_URL: redis://redis/0
depends_on: [redis]
depends_on: [redis, eth-server]
healthWatcher:
image: tornadocash/relayer:mining
eth-healthWatcher:
image: tornadorelayer:mainnet
profiles: ['eth']
restart: always
command: healthWatcher
env_file: .env
env_file: .env.eth
environment:
NET_ID: 1
REDIS_URL: redis://redis/0
depends_on: [redis]
depends_on: [redis, eth-server]
worker1:
image: tornadocash/relayer:mining
eth-worker1:
image: tornadorelayer:mainnet
profiles: ['eth']
restart: always
command: worker
env_file: .env
env_file: .env.eth
environment:
NET_ID: 1
REDIS_URL: redis://redis/0
depends_on: [redis]
depends_on: [redis, eth-server]
# worker2:
# image: tornadocash/relayer:mining
# # This is additional worker for ethereum mainnet
# # So you can process transactions from multiple addresses, but before it you need to set up those addresses as workers
# eth-worker2:
# image: tornadorelayer:mainnet
# profiles: [ 'eth' ]
# restart: always
# command: worker
# env_file: .env
# env_file: .env2.eth
# environment:
# PRIVATE_KEY: qwe
# REDIS_URL: redis://redis/0
# # this container will proxy *.onion domain to the server container
@@ -104,47 +163,294 @@ services:
# RPC_URL: https://mainnet.infura.io
# BLOCK_EXPLORER: etherscan.io
redis:
image: redis
restart: always
command: [redis-server, --appendonly, 'yes']
volumes:
- redis:/data
# -------------------------------------------------- #
nginx:
image: nginx:alpine
container_name: nginx
restart: always
ports:
- 80:80
- 443:443
volumes:
- conf:/etc/nginx/conf.d
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
- certs:/etc/nginx/certs
logging:
driver: none
# ---------------------- BSC (Binance Smart Chain) ----------------------- #
dockergen:
image: poma/docker-gen
container_name: dockergen
restart: always
command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
volumes_from:
- nginx
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
container_name: letsencrypt
bsc-server:
image: tornadorelayer:sidechain
profiles: ['bsc']
restart: always
command: server
env_file: .env.bsc
environment:
NGINX_DOCKER_GEN_CONTAINER: dockergen
volumes_from:
- nginx
- dockergen
NET_ID: 56
REDIS_URL: redis://redis/1
nginx_proxy_read_timeout: 600
depends_on: [redis]
bsc-healthWatcher:
image: tornadorelayer:sidechain
profiles: ['bsc']
restart: always
command: healthWatcher
env_file: .env.bsc
environment:
NET_ID: 56
REDIS_URL: redis://redis/1
depends_on: [redis, bsc-server]
bsc-worker1:
image: tornadorelayer:sidechain
profiles: ['bsc']
restart: always
command: worker
env_file: .env.bsc
environment:
NET_ID: 56
REDIS_URL: redis://redis/1
depends_on: [redis, bsc-server]
# -------------------------------------------------- #
# ---------------------- Polygon (MATIC) --------------------- #
polygon-server:
image: tornadorelayer:sidechain
profiles: ['polygon']
restart: always
command: server
env_file: .env.polygon
environment:
NET_ID: 137
REDIS_URL: redis://redis/2
nginx_proxy_read_timeout: 600
depends_on: [redis]
polygon-healthWatcher:
image: tornadorelayer:sidechain
profiles: ['polygon']
restart: always
command: healthWatcher
env_file: .env.polygon
environment:
NET_ID: 137
REDIS_URL: redis://redis/2
depends_on: [redis, polygon-server]
polygon-worker1:
image: tornadorelayer:sidechain
profiles: ['polygon']
restart: always
command: worker
env_file: .env.polygon
environment:
NET_ID: 137
REDIS_URL: redis://redis/2
depends_on: [redis, polygon-server]
# -------------------------------------------------- #
# ---------------------- Gnosis (XDAI) ---------------------- #
gnosis-server:
image: tornadorelayer:sidechain
profiles: ['gnosis']
restart: always
command: server
env_file: .env.gnosis
environment:
NET_ID: 100
REDIS_URL: redis://redis/3
nginx_proxy_read_timeout: 600
depends_on: [redis]
gnosis-healthWatcher:
image: tornadorelayer:sidechain
profiles: ['gnosis']
restart: always
command: healthWatcher
env_file: .env.gnosis
environment:
NET_ID: 100
REDIS_URL: redis://redis/3
depends_on: [redis, gnosis-server]
gnosis-worker1:
image: tornadorelayer:sidechain
profiles: ['gnosis']
restart: always
command: worker
env_file: .env.gnosis
environment:
NET_ID: 100
REDIS_URL: redis://redis/3
depends_on: [redis, gnosis-server]
# -------------------------------------------------- #
# ---------------------- AVAX ---------------------- #
avax-server:
image: tornadorelayer:sidechain
profiles: ['avax']
restart: always
command: server
env_file: .env.avax
environment:
NET_ID: 43114
REDIS_URL: redis://redis/4
nginx_proxy_read_timeout: 600
depends_on: [redis]
avax-healthWatcher:
image: tornadorelayer:sidechain
profiles: ['avax']
restart: always
command: healthWatcher
env_file: .env.avax
environment:
NET_ID: 43114
REDIS_URL: redis://redis/4
depends_on: [redis, avax-server]
avax-worker1:
image: tornadorelayer:sidechain
profiles: ['avax']
restart: always
command: worker
env_file: .env.avax
environment:
NET_ID: 43114
REDIS_URL: redis://redis/4
depends_on: [redis, avax-server]
# -------------------------------------------------- #
# ---------------------- OP ------------------------ #
op-server:
image: tornadorelayer:sidechain
profiles: ['op']
restart: always
command: server
env_file: .env.op
environment:
NET_ID: 10
REDIS_URL: redis://redis/5
nginx_proxy_read_timeout: 600
depends_on: [redis]
op-healthWatcher:
image: tornadorelayer:sidechain
profiles: ['op']
restart: always
command: healthWatcher
env_file: .env.op
environment:
NET_ID: 10
REDIS_URL: redis://redis/5
depends_on: [redis, op-server]
op-worker1:
image: tornadorelayer:sidechain
profiles: ['op']
restart: always
command: worker
env_file: .env.op
environment:
NET_ID: 10
REDIS_URL: redis://redis/5
depends_on: [redis, op-server]
# -------------------------------------------------- #
# ---------------------- Arbitrum ----------------------- #
arb-server:
image: tornadorelayer:sidechain
profiles: ['arb']
restart: always
command: server
env_file: .env.arb
environment:
NET_ID: 42161
REDIS_URL: redis://redis/6
nginx_proxy_read_timeout: 600
depends_on: [redis]
arb-healthWatcher:
image: tornadorelayer:sidechain
profiles: ['arb']
restart: always
command: healthWatcher
env_file: .env.arb
environment:
NET_ID: 42161
REDIS_URL: redis://redis/6
depends_on: [redis, arb-server]
arb-worker1:
image: tornadorelayer:sidechain
profiles: ['arb']
restart: always
command: worker
env_file: .env.arb
environment:
NET_ID: 42161
REDIS_URL: redis://redis/6
depends_on: [redis, arb-server]
# -------------------------------------------------- #
# ---------------------- Goerli (Ethereum Testnet) ---------------------- #
goerli-server:
image: tornadorelayer:mainnet
profiles: ['geth']
restart: always
command: server
env_file: .env.goerli
environment:
NET_ID: 5
REDIS_URL: redis://redis/7
nginx_proxy_read_timeout: 600
depends_on: [redis]
goerli-treeWatcher:
image: tornadorelayer:mainnet
profiles: ['goerli']
restart: always
command: treeWatcher
env_file: .env.goerli
environment:
NET_ID: 5
REDIS_URL: redis://redis/7
depends_on: [redis, goerli-server]
goerli-priceWatcher:
image: tornadorelayer:mainnet
profiles: ['goerli']
restart: always
command: priceWatcher
env_file: .env.goerli
environment:
NET_ID: 5
REDIS_URL: redis://redis/7
depends_on: [redis, goerli-server]
goerli-healthWatcher:
image: tornadorelayer:mainnet
profiles: ['goerli']
restart: always
command: healthWatcher
env_file: .env.goerli
environment:
NET_ID: 5
REDIS_URL: redis://redis/7
depends_on: [redis, goerli-server]
goerli-worker1:
image: tornadorelayer:mainnet
profiles: ['goerli']
restart: always
command: worker
env_file: .env.goerli
environment:
NET_ID: 5
REDIS_URL: redis://redis/7
depends_on: [redis, goerli-server]
# -------------------------------------------------- #
volumes:
conf:

View File

@@ -0,0 +1 @@
zabbix

View File

@@ -0,0 +1 @@
zabbix

37
monitoring/.env_agent Normal file
View File

@@ -0,0 +1,37 @@
ZBX_HOSTNAME=Zabbix
# ZBX_SOURCEIP=
# ZBX_DEBUGLEVEL=3
# ZBX_ENABLEREMOTECOMMANDS=0 # Deprecated since 5.0.0
# ZBX_LOGREMOTECOMMANDS=0
# ZBX_HOSTINTERFACE= # Available since 4.4.0
# ZBX_HOSTINTERFACEITEM= # Available since 4.4.0
# ZBX_SERVER_HOST=10.110.0.5
# ZBX_PASSIVE_ALLOW=true
# ZBX_PASSIVESERVERS=
# ZBX_ACTIVE_ALLOW=true
# ZBX_ACTIVESERVERS=
# ZBX_LISTENIP=
# ZBX_STARTAGENTS=3
# ZBX_HOSTNAMEITEM=system.hostname
# ZBX_METADATA=
# ZBX_METADATAITEM=
# ZBX_REFRESHACTIVECHECKS=120
# ZBX_BUFFERSEND=5
# ZBX_BUFFERSIZE=100
# ZBX_MAXLINESPERSECOND=20
# ZBX_ALIAS=""
# ZBX_TIMEOUT=3
# ZBX_UNSAFEUSERPARAMETERS=0
# ZBX_LOADMODULE="dummy1.so,dummy2.so,dummy10.so"
# ZBX_TLSCONNECT=unencrypted
# ZBX_TLSACCEPT=unencrypted
# ZBX_TLSCAFILE=
# ZBX_TLSCRLFILE=
# ZBX_TLSSERVERCERTISSUER=
# ZBX_TLSSERVERCERTSUBJECT=
# ZBX_TLSCERTFILE=
# ZBX_TLSKEYFILE=
# ZBX_TLSPSKIDENTITY=
# ZBX_TLSPSKFILE=
# ZBX_DENYKEY=system.run[*]
# ZBX_ALLOWKEY=

9
monitoring/.env_db_pgsql Normal file
View File

@@ -0,0 +1,9 @@
# DB_SERVER_HOST=postgres-server
# DB_SERVER_PORT=5432
# POSTGRES_USER=zabbix
POSTGRES_USER_FILE=/run/secrets/POSTGRES_USER
# POSTGRES_PASSWORD=zabbix
POSTGRES_PASSWORD_FILE=/run/secrets/POSTGRES_PASSWORD
POSTGRES_DB=zabbix
# DB_SERVER_SCHEMA=public
# ENABLE_TIMESCALEDB=tru

60
monitoring/.env_srv Normal file
View File

@@ -0,0 +1,60 @@
# ZBX_LISTENIP=
# ZBX_HISTORYSTORAGEURL=http://elasticsearch:9200/ # Available since 3.4.5
# ZBX_HISTORYSTORAGETYPES=uint,dbl,str,log,text # Available since 3.4.5
# ZBX_DBTLSCONNECT=required # Available since 5.0.0
# ZBX_DBTLSCAFILE=/run/secrets/root-ca.pem # Available since 5.0.0
# ZBX_DBTLSCERTFILE=/run/secrets/client-cert.pem # Available since 5.0.0
# ZBX_DBTLSKEYFILE=/run/secrets/client-key.pem # Available since 5.0.0
# ZBX_DBTLSCIPHER= # Available since 5.0.0
# ZBX_DBTLSCIPHER13= # Available since 5.0.0
# ZBX_DEBUGLEVEL=3
# ZBX_STARTPOLLERS=5
# ZBX_IPMIPOLLERS=0
# ZBX_STARTPREPROCESSORS=3 # Available since 3.4.0
# ZBX_STARTPOLLERSUNREACHABLE=1
# ZBX_STARTTRAPPERS=5
# ZBX_STARTPINGERS=1
# ZBX_STARTDISCOVERERS=1
# ZBX_STARTHTTPPOLLERS=1
# ZBX_STARTTIMERS=1
# ZBX_STARTESCALATORS=1
# ZBX_STARTALERTERS=3 # Available since 3.4.0
# ZBX_JAVAGATEWAY_ENABLE=true
# ZBX_JAVAGATEWAY=zabbix-java-gateway
# ZBX_JAVAGATEWAYPORT=10052
# ZBX_STARTJAVAPOLLERS=5
# ZBX_STARTVMWARECOLLECTORS=0
# ZBX_VMWAREFREQUENCY=60
# ZBX_VMWAREPERFFREQUENCY=60
# ZBX_VMWARECACHESIZE=8M
# ZBX_VMWARETIMEOUT=10
# ZBX_ENABLE_SNMP_TRAPS=true
# ZBX_SOURCEIP=
# ZBX_HOUSEKEEPINGFREQUENCY=1
# ZBX_MAXHOUSEKEEPERDELETE=5000
# ZBX_SENDERFREQUENCY=30
# ZBX_CACHESIZE=8M
# ZBX_CACHEUPDATEFREQUENCY=60
# ZBX_STARTDBSYNCERS=4
# ZBX_HISTORYCACHESIZE=16M
# ZBX_HISTORYINDEXCACHESIZE=4M
# ZBX_TRENDCACHESIZE=4M
# ZBX_VALUECACHESIZE=8M
# ZBX_TIMEOUT=4
# ZBX_TRAPPERIMEOUT=300
# ZBX_UNREACHABLEPERIOD=45
# ZBX_UNAVAILABLEDELAY=60
# ZBX_UNREACHABLEDELAY=15
# ZBX_LOGSLOWQUERIES=3000
# ZBX_EXPORTFILESIZE=
# ZBX_STARTPROXYPOLLERS=1
# ZBX_PROXYCONFIGFREQUENCY=3600
# ZBX_PROXYDATAFREQUENCY=1
# ZBX_LOADMODULE="dummy1.so,dummy2.so,dummy10.so"
# ZBX_TLSCAFILE=
# ZBX_TLSCRLFILE=
# ZBX_TLSCERTFILE=
# ZBX_TLSKEYFILE=
# ZBX_VAULTDBPATH=
# ZBX_VAULTURL=https://127.0.0.1:8200
# VAULT_TOKEN=

26
monitoring/.env_web Normal file
View File

@@ -0,0 +1,26 @@
# ZBX_SERVER_HOST=zabbix-server
# ZBX_SERVER_PORT=10051
# ZBX_SERVER_NAME=Monitoring
# ZBX_DB_ENCRYPTION=true # Available since 5.0.0
# ZBX_DB_KEY_FILE=/run/secrets/client-key.pem # Available since 5.0.0
# ZBX_DB_CERT_FILE=/run/secrets/client-cert.pem # Available since 5.0.0
# ZBX_DB_CA_FILE=/run/secrets/root-ca.pem # Available since 5.0.0
# ZBX_DB_VERIFY_HOST=false # Available since 5.0.0
# ZBX_DB_CIPHER_LIST= # Available since 5.0.0
# ZBX_VAULTDBPATH=
# ZBX_VAULTURL=https://127.0.0.1:8200
# VAULT_TOKEN=
# ZBX_HISTORYSTORAGEURL=http://elasticsearch:9200/ # Available since 3.4.5
# ZBX_HISTORYSTORAGETYPES=['uint', 'dbl', 'str', 'text', 'log'] # Available since 3.4.5
# ENABLE_WEB_ACCESS_LOG=true
# ZBX_MAXEXECUTIONTIME=600
# ZBX_MEMORYLIMIT=128M
# ZBX_POSTMAXSIZE=16M
# ZBX_UPLOADMAXFILESIZE=2M
# ZBX_MAXINPUTTIME=300
# ZBX_SESSION_NAME=zbx_sessionid
# Timezone one of: http://php.net/manual/en/timezones.php
# PHP_TZ=Europe/Riga
# ZBX_DENY_GUI_ACCESS=false
# ZBX_GUI_ACCESS_IP_RANGE=['127.0.0.1']
# ZBX_GUI_WARNING_MSG=Zabbix is under maintenance.

67
monitoring/README.md Normal file
View File

@@ -0,0 +1,67 @@
# Installing the Zabbix server
Change default passwords, ports and set listen IP (ports `8080/tcp` and `10051/tcp` will be open on all interfaces, use a firewall or specify the address of the required interface), then run:
```bash
wget https://github.com/tornadocash/tornado-relayer/raw/master/monitoring/zabbix.tar.gz
mkdir $HOME/monitoring/
tar -xzf zabbix.tar.gz -C $HOME/monitoring/
cd $HOME/monitoring/
docker-compose up -d
```
# Installing the Zabbix agent
Download package from repository [https://repo.zabbix.com/zabbix/5.2/ubuntu/pool/main/z/zabbix/](https://repo.zabbix.com/zabbix/5.2/ubuntu/pool/main/z/zabbix/) and run:
```bash
sudo dpkg -i zabbix-agent_5.2.*.deb
sudo usermod -aG docker zabbix
```
Change default values in `/etc/zabbix/zabbix_agent2.conf`:
- `Hostname` the same as in the zabbix-server web interface;
- `Server` and `ServerActive` set zabbix server IP or DNS name;
- `ListenIP` to local network IP available from zabbix server or set firewall rules to restrict access to port `10050`;
- uncomment `Plugins.Docker.Endpoint=unix:///var/run/docker.sock`.
Then run:
```bash
sudo systemctl enable zabbix-agent2.service
sudo systemctl restart zabbix-agent2.service
```
# Adding the host
Log into your Zabbix server (defaul login and passord: `Admin` - `zabbix`) and click on the Configuration tab and then the Hosts tab. Click the Create host button near the top right corner. In the resulting page, change the Host name and IP ADDRESS sections to match the information for your remote server. Set `{$URL}` macros to relayer host, example `http://localhost/v1/status` or `https://domain.name/v1/status`.
# Import templates
Import templates using the WebUI:
- [Docker-template.yaml](/monitoring/templates/Docker-template.yaml);
- [Tornado-relayer-template.yaml](/monitoring/templates/Tornado-relayer-template.yaml).
Link templates with added host. It is also recommended to link `Linux CPU by Zabbix agent`, `Linux filesystems by Zabbix agent` and `Linux memory by Zabbix agent` templates to the host.
# Alerts
In WebUI - Administration -> Media types -> Telegram:
```
https://git.zabbix.com/projects/ZBX/repos/zabbix/browse/templates/media/telegram
1. Register bot: send "/newbot" to @BotFather and follow instructions
2. Copy and paste the obtained token into the "Token" field above
3. If you want to send personal notifications, you need to get chat id of the user you want to send messages to:
3.1. Send "/getid" to "@myidbot" in Telegram messenger
3.2. Copy returned chat id and save it in the "Telegram Webhook" media for the user
3.3. Ask the user to send "/start" to your bot (Telegram bot won't send anything to the user without it)
4. If you want to send group notifications, you need to get group id of the group you want to send messages to:
4.1. Add "@myidbot" to your group
4.2. Send "/getgroupid@myidbot" in your group
4.3. Copy returned group id save it in the "Telegram Webhook" media for the user you created for group notifications
4.4. Send "/start@your_bot_name_here" in your group (Telegram bot won't send anything to the group without it)
```

View File

@@ -0,0 +1,186 @@
# Restrict access to 10051/tcp on public ip
version: '3.5'
services:
zabbix-server:
image: zabbix/zabbix-server-pgsql:alpine-5.2-latest
restart: always
ports:
- '10051:10051'
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./zbx_env/usr/lib/zabbix/alertscripts:/usr/lib/zabbix/alertscripts:ro
- ./zbx_env/usr/lib/zabbix/externalscripts:/usr/lib/zabbix/externalscripts:ro
- ./zbx_env/var/lib/zabbix/export:/var/lib/zabbix/export:rw
- ./zbx_env/var/lib/zabbix/modules:/var/lib/zabbix/modules:ro
- ./zbx_env/var/lib/zabbix/enc:/var/lib/zabbix/enc:ro
- ./zbx_env/var/lib/zabbix/ssh_keys:/var/lib/zabbix/ssh_keys:ro
- ./zbx_env/var/lib/zabbix/mibs:/var/lib/zabbix/mibs:ro
- ./zbx_env/var/lib/zabbix/snmptraps:/var/lib/zabbix/snmptraps:ro
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 40000
deploy:
resources:
limits:
cpus: '0.70'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
env_file:
- .env_db_pgsql
- .env_srv
secrets:
- POSTGRES_USER
- POSTGRES_PASSWORD
depends_on:
- postgres-server
networks:
zbx_net_backend:
aliases:
- zabbix-server
- zabbix-server-pgsql
- zabbix-server-alpine-pgsql
- zabbix-server-pgsql-alpine
zbx_net_frontend:
stop_grace_period: 30s
sysctls:
- net.ipv4.ip_local_port_range=1024 65000
- net.ipv4.conf.all.accept_redirects=0
- net.ipv4.conf.all.secure_redirects=0
- net.ipv4.conf.all.send_redirects=0
labels:
com.zabbix.description: 'Zabbix server with PostgreSQL database support'
com.zabbix.company: 'Zabbix LLC'
com.zabbix.component: 'zabbix-server'
com.zabbix.dbtype: 'pgsql'
com.zabbix.os: 'alpine'
zabbix-web:
image: zabbix/zabbix-web-nginx-pgsql:alpine-5.2-latest
restart: always
ports:
- '8080:8080'
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./zbx_env/etc/ssl/nginx:/etc/ssl/nginx:ro
- ./zbx_env/usr/share/zabbix/modules/:/usr/share/zabbix/modules/:ro
deploy:
resources:
limits:
cpus: '0.70'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
env_file:
- .env_db_pgsql
- .env_web
secrets:
- POSTGRES_USER
- POSTGRES_PASSWORD
depends_on:
- postgres-server
- zabbix-server
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:8080/']
interval: 10s
timeout: 5s
retries: 3
networks:
zbx_net_backend:
aliases:
- zabbix-web-nginx-pgsql
- zabbix-web-nginx-alpine-pgsql
- zabbix-web-nginx-pgsql-alpine
zbx_net_frontend:
stop_grace_period: 10s
sysctls:
- net.core.somaxconn=65535
labels:
com.zabbix.description: 'Zabbix frontend on Nginx web-server with PostgreSQL database support'
com.zabbix.company: 'Zabbix LLC'
com.zabbix.component: 'zabbix-frontend'
com.zabbix.webserver: 'nginx'
com.zabbix.dbtype: 'pgsql'
com.zabbix.os: 'alpine'
zabbix-agent:
image: zabbix/zabbix-agent2:alpine-5.2-latest
restart: always
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- /var/run/docker.sock:/var/run/docker.sock
env_file:
- .env_agent
privileged: true
user: root
pid: 'host'
networks:
zbx_net_backend:
aliases:
- zabbix-agent
- zabbix-agent-passive
- zabbix-agent-alpine
stop_grace_period: 5s
postgres-server:
image: postgres:alpine
restart: always
volumes:
- ./zbx_env/var/lib/postgresql/data:/var/lib/postgresql/data:rw
env_file:
- .env_db_pgsql
secrets:
- POSTGRES_USER
- POSTGRES_PASSWORD
stop_grace_period: 1m
networks:
zbx_net_backend:
aliases:
- postgres-server
- pgsql-server
- pgsql-database
portainer:
image: portainer/portainer:latest
restart: always
ports:
- '9000:9000'
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer-data:/data
networks:
zbx_net_frontend:
driver: bridge
driver_opts:
com.docker.network.enable_ipv6: 'false'
ipam:
driver: default
config:
- subnet: 172.16.238.0/24
zbx_net_backend:
driver: bridge
driver_opts:
com.docker.network.enable_ipv6: 'false'
internal: true
ipam:
driver: default
config:
- subnet: 172.16.239.0/24
secrets:
POSTGRES_USER:
file: ./.POSTGRES_USER
POSTGRES_PASSWORD:
file: ./.POSTGRES_PASSWORD
volumes:
portainer-data:

View File

@@ -0,0 +1,393 @@
zabbix_export:
version: '5.2'
date: '2021-11-29T12:29:17Z'
groups:
- name: Docker
templates:
- template: Docker
name: Docker
description: |
Get Docker engine metrics from plugin for the New Zabbix Agent (zabbix-agent2).
You can discuss this template or leave feedback on our forum
Template tooling version used: 0.38
groups:
- name: Docker
applications:
- name: Docker
- name: 'Zabbix raw items'
items:
- name: 'Docker: Get containers'
key: docker.containers
history: '0'
trends: '0'
value_type: TEXT
applications:
- name: 'Zabbix raw items'
- name: 'Docker: Containers paused'
type: DEPENDENT
key: docker.containers.paused
delay: '0'
history: 7d
description: 'Total number of containers paused on this host'
applications:
- name: Docker
preprocessing:
- type: JSONPATH
parameters:
- $.ContainersPaused
master_item:
key: docker.info
- name: 'Docker: Containers running'
type: DEPENDENT
key: docker.containers.running
delay: '0'
history: 7d
description: 'Total number of containers running on this host'
applications:
- name: Docker
preprocessing:
- type: JSONPATH
parameters:
- $.ContainersRunning
master_item:
key: docker.info
- name: 'Docker: Containers stopped'
type: DEPENDENT
key: docker.containers.stopped
delay: '0'
history: 7d
description: 'Total number of containers stopped on this host'
applications:
- name: Docker
preprocessing:
- type: JSONPATH
parameters:
- $.ContainersStopped
master_item:
key: docker.info
triggers:
- expression: '{avg(5m)}>=1'
name: 'Docker: containers is stopped'
priority: HIGH
- name: 'Docker: Containers total'
type: DEPENDENT
key: docker.containers.total
delay: '0'
history: 7d
description: 'Total number of containers on this host'
applications:
- name: Docker
preprocessing:
- type: JSONPATH
parameters:
- $.Containers
master_item:
key: docker.info
- name: 'Docker: Get images'
key: docker.images
history: '0'
trends: '0'
status: DISABLED
value_type: TEXT
applications:
- name: 'Zabbix raw items'
- name: 'Docker: Get info'
key: docker.info
history: '0'
trends: '0'
value_type: TEXT
applications:
- name: 'Zabbix raw items'
- name: 'Docker: Memory total'
type: DEPENDENT
key: docker.mem.total
delay: '0'
history: 7d
status: DISABLED
units: B
applications:
- name: Docker
preprocessing:
- type: JSONPATH
parameters:
- $.MemTotal
master_item:
key: docker.info
- name: 'Docker: Ping'
key: docker.ping
history: 7h
applications:
- name: Docker
valuemap:
name: 'Service state'
preprocessing:
- type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 10m
triggers:
- expression: '{last()}=0'
name: 'Docker: Service is down'
priority: AVERAGE
manual_close: 'YES'
discovery_rules:
- name: 'Containers discovery'
key: 'docker.containers.discovery[true]'
delay: 15m
filter:
evaltype: AND
conditions:
- macro: '{#NAME}'
value: '{$DOCKER.LLD.FILTER.CONTAINER.MATCHES}'
formulaid: A
- macro: '{#NAME}'
value: '{$DOCKER.LLD.FILTER.CONTAINER.NOT_MATCHES}'
operator: NOT_MATCHES_REGEX
formulaid: B
description: |
Discovery for containers metrics
Parameter:
true - Returns all containers
false - Returns only running containers
item_prototypes:
- name: 'Container {#NAME}: Finished at'
type: DEPENDENT
key: 'docker.container_info.finished["{#NAME}"]'
delay: '0'
history: 7d
value_type: FLOAT
units: unixtime
application_prototypes:
- name: 'Docker: Container {#NAME}'
preprocessing:
- type: JSONPATH
parameters:
- $.State.FinishedAt
- type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 1d
master_item:
key: 'docker.container_info["{#NAME}"]'
- name: 'Container {#NAME}: Restart count'
type: DEPENDENT
key: 'docker.container_info.restart_count["{#NAME}"]'
delay: '0'
history: 7d
application_prototypes:
- name: 'Docker: Container {#NAME}'
preprocessing:
- type: JSONPATH
parameters:
- $.RestartCount
master_item:
key: 'docker.container_info["{#NAME}"]'
trigger_prototypes:
- expression: '{last()}>5'
name: 'Container {#NAME}: restarting constantly'
opdata: '{ITEM.VALUE}'
priority: HIGH
- name: 'Container {#NAME}: Started at'
type: DEPENDENT
key: 'docker.container_info.started["{#NAME}"]'
delay: '0'
history: 7d
value_type: FLOAT
units: unixtime
application_prototypes:
- name: 'Docker: Container {#NAME}'
preprocessing:
- type: JSONPATH
parameters:
- $.State.StartedAt
- type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 1d
master_item:
key: 'docker.container_info["{#NAME}"]'
- name: 'Container {#NAME}: Error'
type: DEPENDENT
key: 'docker.container_info.state.error["{#NAME}"]'
delay: '0'
history: 7d
trends: '0'
value_type: CHAR
application_prototypes:
- name: 'Docker: Container {#NAME}'
preprocessing:
- type: JSONPATH
parameters:
- $.State.Error
- type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 1d
master_item:
key: 'docker.container_info["{#NAME}"]'
trigger_prototypes:
- expression: '{diff()}=1 and {strlen()}>0'
name: 'Container {#NAME}: An error has occurred in the container'
priority: WARNING
description: 'Container {#NAME} has an error. Ack to close.'
manual_close: 'YES'
- name: 'Container {#NAME}: Exit code'
type: DEPENDENT
key: 'docker.container_info.state.exitcode["{#NAME}"]'
delay: '0'
history: 7d
application_prototypes:
- name: 'Docker: Container {#NAME}'
preprocessing:
- type: JSONPATH
parameters:
- $.State.ExitCode
- type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 1d
master_item:
key: 'docker.container_info["{#NAME}"]'
- name: 'Container {#NAME}: Paused'
type: DEPENDENT
key: 'docker.container_info.state.paused["{#NAME}"]'
delay: '0'
history: 7d
application_prototypes:
- name: 'Docker: Container {#NAME}'
valuemap:
name: 'Docker flag'
preprocessing:
- type: JSONPATH
parameters:
- $.State.Paused
- type: BOOL_TO_DECIMAL
parameters:
- ''
master_item:
key: 'docker.container_info["{#NAME}"]'
- name: 'Container {#NAME}: Restarting'
type: DEPENDENT
key: 'docker.container_info.state.restarting["{#NAME}"]'
delay: '0'
history: 7d
application_prototypes:
- name: 'Docker: Container {#NAME}'
valuemap:
name: 'Docker flag'
preprocessing:
- type: JSONPATH
parameters:
- $.State.Restarting
- type: BOOL_TO_DECIMAL
parameters:
- ''
master_item:
key: 'docker.container_info["{#NAME}"]'
- name: 'Container {#NAME}: Running'
type: DEPENDENT
key: 'docker.container_info.state.running["{#NAME}"]'
delay: '0'
history: 7d
application_prototypes:
- name: 'Docker: Container {#NAME}'
valuemap:
name: 'Docker flag'
preprocessing:
- type: JSONPATH
parameters:
- $.State.Running
- type: BOOL_TO_DECIMAL
parameters:
- ''
master_item:
key: 'docker.container_info["{#NAME}"]'
- name: 'Container {#NAME}: Status'
type: DEPENDENT
key: 'docker.container_info.state.status["{#NAME}"]'
delay: '0'
history: 7d
trends: '0'
value_type: CHAR
application_prototypes:
- name: 'Docker: Container {#NAME}'
preprocessing:
- type: JSONPATH
parameters:
- $.State.Status
- type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 1h
master_item:
key: 'docker.container_info["{#NAME}"]'
- name: 'Container {#NAME}: Get info'
key: 'docker.container_info["{#NAME}"]'
history: '0'
trends: '0'
value_type: CHAR
description: 'Return low-level information about a container'
application_prototypes:
- name: 'Docker: Container {#NAME}'
trigger_prototypes:
- expression: '{Docker:docker.container_info.state.exitcode["{#NAME}"].last()}>0 and {Docker:docker.container_info.state.running["{#NAME}"].last()}=0'
name: 'Container {#NAME}: Container has been stopped with error code'
opdata: 'Exit code: {ITEM.LASTVALUE1}'
priority: AVERAGE
manual_close: 'YES'
macros:
- macro: '{$DOCKER.LLD.FILTER.CONTAINER.MATCHES}'
value: '.*'
description: 'Filter of discoverable containers'
- macro: '{$DOCKER.LLD.FILTER.CONTAINER.NOT_MATCHES}'
value: CHANGE_IF_NEEDED
description: 'Filter to exclude discovered containers'
- macro: '{$DOCKER.LLD.FILTER.IMAGE.MATCHES}'
value: '.*'
description: 'Filter of discoverable images'
- macro: '{$DOCKER.LLD.FILTER.IMAGE.NOT_MATCHES}'
value: CHANGE_IF_NEEDED
description: 'Filter to exclude discovered images'
graphs:
- name: 'Docker: Containers'
graph_items:
- drawtype: GRADIENT_LINE
color: 1A7C11
item:
host: Docker
key: docker.containers.running
- sortorder: '1'
drawtype: BOLD_LINE
color: 2774A4
item:
host: Docker
key: docker.containers.paused
- sortorder: '2'
drawtype: BOLD_LINE
color: F63100
item:
host: Docker
key: docker.containers.stopped
- sortorder: '3'
drawtype: BOLD_LINE
color: A54F10
item:
host: Docker
key: docker.containers.total
- name: 'Docker: Memory total'
graph_items:
- drawtype: BOLD_LINE
color: 1A7C11
item:
host: Docker
key: docker.mem.total
value_maps:
- name: 'Docker flag'
mappings:
- value: '0'
newvalue: 'False'
- value: '1'
newvalue: 'True'
- name: 'Service state'
mappings:
- value: '0'
newvalue: Down
- value: '1'
newvalue: Up

View File

@@ -0,0 +1,70 @@
zabbix_export:
version: '5.2'
date: '2021-12-01T13:26:59Z'
groups:
- name: Templates/Applications
templates:
- template: Tornado-relayer
name: Tornado-relayer
groups:
- name: Templates/Applications
items:
- name: 'tornado-relayer: health.error'
type: DEPENDENT
key: tornado-relayer.health.error
delay: '0'
trends: '0'
value_type: TEXT
preprocessing:
- type: JSONPATH
parameters:
- $.health.error
master_item:
key: 'web.page.get[{$URL}]'
triggers:
- expression: '{last()}<>""'
name: 'tornado-relayer: health error'
priority: AVERAGE
- name: 'tornado-relayer: health.status'
type: DEPENDENT
key: tornado-relayer.health.status
delay: '0'
trends: '0'
value_type: TEXT
preprocessing:
- type: JSONPATH
parameters:
- $.health.status
master_item:
key: 'web.page.get[{$URL}]'
triggers:
- expression: '{last(#3)}<>"true"'
name: 'tornado-relayer: health status <> true'
priority: HIGH
- name: 'tornado-relayer: data'
type: ZABBIX_ACTIVE
key: 'web.page.get[{$URL}]'
history: '0'
trends: '0'
value_type: TEXT
preprocessing:
- type: REGEX
parameters:
- '\n\s?\n([\s\S]*)'
- \1
httptests:
- name: 'tornado-relayer: status page'
agent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/80.0.3987.87 Chrome/80.0.3987.87 Safari/537.36'
steps:
- name: 'status page'
url: '{$URL}'
follow_redirects: 'NO'
required: status
status_codes: '200'
triggers:
- expression: '{Tornado-relayer:web.test.fail[tornado-relayer: status page].last()}>0'
name: 'tornado-relayer: status page failed'
priority: AVERAGE
- expression: '{Tornado-relayer:web.test.rspcode[tornado-relayer: status page,status page].last(#3)}<>200'
name: 'tornado-relayer: status page rspcode <>200'
priority: HIGH

BIN
monitoring/zabbix.tar.gz Normal file

Binary file not shown.

View File

@@ -1,7 +1,7 @@
{
"name": "relay",
"version": "4.0.15",
"description": "Relayer for Tornado.cash privacy solution. https://tornado.cash",
"version": "5.2.0",
"description": "Relayer for Tornado.cash privacy solution.",
"scripts": {
"server": "node src/server.js",
"worker": "node src/worker",
@@ -13,25 +13,27 @@
"prettier:fix": "npx prettier --write . --config .prettierrc",
"lint": "yarn eslint && yarn prettier:check",
"test": "mocha",
"start": "yarn server & yarn priceWatcher & yarn treeWatcher & yarn worker & yarn healthWatcher"
"build": "docker build -t tornadocash/relayer:mainnet-v5 .",
"start": "docker-compose up -d redis && concurrently \"yarn server\" \"yarn priceWatcher\" \"yarn treeWatcher\" \"yarn worker\" \"yarn healthWatcher\""
},
"author": "tornado.cash",
"author": "Tornado Cash team",
"license": "MIT",
"dependencies": {
"@tornado/anonymity-mining": "^2.1.5",
"@tornado/circomlib": "^0.0.21",
"@tornado/fixed-merkle-tree": "0.4.0",
"@tornado/tornado-config": "^1",
"@tornado/tornado-oracles": "^3.3.0",
"@tornado/tx-manager": "^0.4.9",
"ajv": "^6.12.5",
"async-mutex": "^0.2.4",
"bull": "^3.12.1",
"circomlib": "git+https://github.com/tornadocash/circomlib.git#3b492f9801573eebcfe1b6c584afe8a3beecf2b4",
"concurrently": "^8.2.0",
"dotenv": "^8.2.0",
"eth-ens-namehash": "^2.0.8",
"express": "^4.17.1",
"fixed-merkle-tree": "^0.4.0",
"gas-price-oracle": "^0.2.2",
"ioredis": "^4.14.1",
"node-fetch": "^2.6.0",
"torn-token": "1.0.4",
"tornado-anonymity-mining": "^2.1.2",
"tx-manager": "^0.2.9",
"node-fetch": "^2.6.7",
"uuid": "^8.3.0",
"web3": "^1.3.0",
"web3-core-promievent": "^1.3.0",

View File

@@ -1,29 +1,27 @@
require('dotenv').config()
const { jobType } = require('./constants')
const tornConfig = require('torn-token')
const tornConfig = require('@tornado/tornado-config')
module.exports = {
netId: Number(process.env.NET_ID) || 1,
redisUrl: process.env.REDIS_URL || 'redis://127.0.0.1:6379',
httpRpcUrl: process.env.HTTP_RPC_URL,
wsRpcUrl: process.env.WS_RPC_URL,
oracleRpcUrl: process.env.ORACLE_RPC_URL || 'https://mainnet.infura.io/',
offchainOracleAddress: '0x080AB73787A8B13EC7F40bd7d00d6CC07F9b24d0',
oracleRpcUrl: process.env.ORACLE_RPC_URL || 'https://api.securerpc.com/v1',
aggregatorAddress: process.env.AGGREGATOR,
minerMerkleTreeHeight: 20,
privateKey: process.env.PRIVATE_KEY,
instances: tornConfig.instances,
torn: tornConfig,
port: process.env.APP_PORT || 8000,
tornadoServiceFee: Number(process.env.REGULAR_TORNADO_WITHDRAW_FEE),
miningServiceFee: Number(process.env.MINING_SERVICE_FEE),
tornadoServiceFee: Number(process.env.RELAYER_FEE),
rewardAccount: process.env.REWARD_ACCOUNT,
governanceAddress: '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
tornadoGoerliProxy: '0x454d870a72e29d5E5697f635128D18077BD04C60',
gasLimits: {
[jobType.TORNADO_WITHDRAW]: 390000,
WITHDRAW_WITH_EXTRA: 480000,
[jobType.MINING_REWARD]: 455000,
[jobType.MINING_WITHDRAW]: 400000,
},
minimumBalance: '1000000000000000000',
minimumBalance: '500000000000000000',
baseFeeReserve: Number(process.env.BASE_FEE_RESERVE_PERCENTAGE),
}

View File

@@ -2,9 +2,9 @@ const {
getTornadoWithdrawInputError,
getMiningRewardInputError,
getMiningWithdrawInputError,
} = require('./validator')
const { postJob } = require('./queue')
const { jobType } = require('./constants')
} = require('../modules/validator')
const { postJob } = require('../queue')
const { jobType } = require('../constants')
async function tornadoWithdraw(req, res) {
const inputError = getTornadoWithdrawInputError(req.body)

4
src/contollers/index.js Normal file
View File

@@ -0,0 +1,4 @@
module.exports = {
controller: require('./controller'),
status: require('./status'),
}

View File

@@ -1,18 +1,18 @@
const queue = require('./queue')
const { netId, tornadoServiceFee, miningServiceFee, instances, redisUrl, rewardAccount } = require('./config')
const { version } = require('../package.json')
const Redis = require('ioredis')
const redis = new Redis(redisUrl)
const queue = require('../queue')
const { netId, tornadoServiceFee, miningServiceFee, instances, rewardAccount } = require('../config')
const { version } = require('../../package.json')
const { redis } = require('../modules/redis')
const { readRelayerErrors } = require('../utils')
async function status(req, res) {
const ethPrices = await redis.hgetall('prices')
const health = await redis.hgetall('health')
health.errorsLog = await readRelayerErrors(redis)
const { waiting: currentQueue } = await queue.queue.getJobCounts()
res.json({
rewardAccount,
instances: instances[`netId${netId}`],
instances: instances[netId],
netId,
ethPrices,
tornadoServiceFee,
@@ -25,7 +25,7 @@ async function status(req, res) {
function index(req, res) {
res.send(
'This is <a href=https://tornado.cash>tornado.cash</a> Relayer service. Check the <a href=/v1/status>/status</a> for settings',
'This is <a href=https://tornado.ws>Tornado Cash</a> Relayer service. Check the <a href=/v1/status>/status</a> for settings',
)
}

View File

@@ -1,20 +1,14 @@
const Web3 = require('web3')
const Redis = require('ioredis')
const { toBN, fromWei } = require('web3-utils')
const { setSafeInterval } = require('./utils')
const { redisUrl, httpRpcUrl, privateKey, minimumBalance } = require('./config')
const web3 = new Web3(httpRpcUrl)
const redis = new Redis(redisUrl)
const { setSafeInterval, toBN, fromWei, RelayerError } = require('./utils')
const { privateKey, minimumBalance } = require('./config')
const { redis } = require('./modules/redis')
const web3 = require('./modules/web3')()
async function main() {
try {
const { address } = web3.eth.accounts.privateKeyToAccount(privateKey)
const balance = await web3.eth.getBalance(address)
if (toBN(balance).lt(toBN(minimumBalance))) {
throw new Error(`Not enough balance, less than ${fromWei(minimumBalance)} ETH`)
throw new RelayerError(`Not enough balance, less than ${fromWei(minimumBalance)} ETH`, 1)
}
await redis.hset('health', { status: true, error: '' })

11
src/modules/redis.js Normal file
View File

@@ -0,0 +1,11 @@
const { createClient } = require('ioredis')
const { redisUrl } = require('../config')
const redis = createClient(redisUrl)
const redisSubscribe = createClient(redisUrl)
module.exports = {
redis,
redisSubscribe,
redisUrl,
}

View File

@@ -1,7 +1,7 @@
const { httpRpcUrl, aggregatorAddress } = require('./config')
const Web3 = require('web3')
const web3 = new Web3(httpRpcUrl)
const aggregator = new web3.eth.Contract(require('../abis/Aggregator.abi.json'), aggregatorAddress)
const { aggregatorAddress } = require('../config')
const web3 = require('./web3')()
const aggregator = new web3.eth.Contract(require('../../abis/Aggregator.abi.json'), aggregatorAddress)
const ens = require('eth-ens-namehash')
class ENSResolver {
@@ -26,5 +26,4 @@ class ENSResolver {
return addresses.length === 1 ? addresses[0] : addresses
}
}
module.exports = ENSResolver
module.exports = new ENSResolver()

View File

@@ -1,6 +1,6 @@
const { isAddress, toChecksumAddress } = require('web3-utils')
const { getInstance } = require('./utils')
const { rewardAccount } = require('./config')
const { getInstance } = require('../utils')
const { rewardAccount } = require('../config')
const Ajv = require('ajv')
const ajv = new Ajv({ format: 'fast' })
@@ -19,7 +19,7 @@ ajv.addKeyword('isAddress', {
ajv.addKeyword('isKnownContract', {
validate: (schema, data) => {
try {
return getInstance(data) !== null
return !!getInstance(data)
} catch (e) {
return false
}

30
src/modules/web3.js Normal file
View File

@@ -0,0 +1,30 @@
const Web3 = require('web3')
const { oracleRpcUrl, httpRpcUrl, wsRpcUrl } = require('../config')
const getWeb3 = (type = 'http') => {
let url
switch (type) {
case 'oracle':
url = oracleRpcUrl
break
case 'ws':
url = wsRpcUrl
return new Web3(
new Web3.providers.WebsocketProvider(wsRpcUrl, {
clientConfig: {
maxReceivedFrameSize: 100000000,
maxReceivedMessageSize: 100000000,
},
}),
)
case 'http':
default:
url = httpRpcUrl
break
}
return new Web3(
new Web3.providers.HttpProvider(url, {
timeout: 200000, // ms
}),
)
}
module.exports = getWeb3

View File

@@ -1,42 +1,21 @@
const Redis = require('ioredis')
const { redisUrl, offchainOracleAddress, oracleRpcUrl } = require('./config')
const { getArgsForOracle, setSafeInterval } = require('./utils')
const redis = new Redis(redisUrl)
const Web3 = require('web3')
const web3 = new Web3(
new Web3.providers.HttpProvider(oracleRpcUrl, {
timeout: 200000, // ms
}),
)
const { setSafeInterval, RelayerError, logRelayerError } = require('./utils')
const { redis } = require('./modules/redis')
const { TokenPriceOracle } = require('@tornado/tornado-oracles')
const { oracleRpcUrl } = require('./config')
const offchainOracleABI = require('../abis/OffchainOracle.abi.json')
const offchainOracle = new web3.eth.Contract(offchainOracleABI, offchainOracleAddress)
const { tokenAddresses, oneUintAmount, currencyLookup } = getArgsForOracle()
const { toBN } = require('web3-utils')
const priceOracle = new TokenPriceOracle(oracleRpcUrl)
async function main() {
try {
const ethPrices = {}
for (let i = 0; i < tokenAddresses.length; i++) {
try {
const price = await offchainOracle.methods
.getRate(tokenAddresses[i], '0x0000000000000000000000000000000000000000')
.call()
const numerator = toBN(oneUintAmount[i])
const denominator = toBN(10).pow(toBN(18)) // eth decimals
const priceFormatted = toBN(price).mul(numerator).div(denominator)
ethPrices[currencyLookup[tokenAddresses[i]]] = priceFormatted.toString()
} catch (e) {
console.error('cant get price of ', tokenAddresses[i])
}
const ethPrices = await priceOracle.fetchPrices()
if (!Object.values(ethPrices).length) {
throw new RelayerError('Can`t update prices', 1)
}
await redis.hmset('prices', ethPrices)
console.log('Wrote following prices to redis', ethPrices)
} catch (e) {
await logRelayerError(redis, e)
console.error('priceWatcher error', e)
}
}

View File

@@ -1,11 +1,11 @@
const { v4: uuid } = require('uuid')
const Queue = require('bull')
const Redis = require('ioredis')
const { redisUrl } = require('./config')
const { status } = require('./constants')
const redis = new Redis(redisUrl)
const queue = new Queue('proofs', redisUrl, {
const { netId } = require('./config')
const { status } = require('./constants')
const { redis, redisUrl } = require('./modules/redis')
const queue = new Queue(`proofs_${netId}`, redisUrl, {
lockDuration: 300000, // Key expiration time for job locks.
lockRenewTime: 30000, // Interval on which to acquire the job lock
stalledInterval: 30000, // How often check for stalled jobs (use 0 for never checking).

30
src/router.js Normal file
View File

@@ -0,0 +1,30 @@
const { controller, status } = require('./contollers')
const router = require('express').Router()
// Add CORS headers
router.use((req, res, next) => {
res.header('X-Frame-Options', 'DENY')
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
next()
})
// Log error to console but don't send it to the client to avoid leaking data
router.use((err, req, res, next) => {
if (err) {
console.error(err)
return res.sendStatus(500)
}
next()
})
router.get('/', status.index)
router.get('/v1/status', status.status)
router.get('/v1/jobs/:id', status.getJob)
router.post('/v1/tornadoWithdraw', controller.tornadoWithdraw)
router.get('/status', status.status)
router.post('/relay', controller.tornadoWithdraw)
router.post('/v1/miningReward', controller.miningReward)
router.post('/v1/miningWithdraw', controller.miningWithdraw)
module.exports = router

View File

@@ -1,41 +1,14 @@
const express = require('express')
const status = require('./status')
const controller = require('./controller')
const { port, rewardAccount } = require('./config')
const { version } = require('../package.json')
const { isAddress } = require('web3-utils')
const app = express()
app.use(express.json())
// Add CORS headers
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
next()
})
// Log error to console but don't send it to the client to avoid leaking data
app.use((err, req, res, next) => {
if (err) {
console.error(err)
return res.sendStatus(500)
}
next()
})
app.get('/', status.index)
app.get('/v1/status', status.status)
app.get('/v1/jobs/:id', status.getJob)
app.post('/v1/tornadoWithdraw', controller.tornadoWithdraw)
app.get('/status', status.status)
app.post('/relay', controller.tornadoWithdraw)
app.post('/v1/miningReward', controller.miningReward)
app.post('/v1/miningWithdraw', controller.miningWithdraw)
const { isAddress } = require('./utils')
const router = require('./router')
if (!isAddress(rewardAccount)) {
throw new Error('No REWARD_ACCOUNT specified')
}
const app = express()
app.use(express.json())
app.use(router)
app.listen(port)
console.log(`Relayer ${version} started on port ${port}`)

View File

@@ -1,39 +1,35 @@
const MerkleTree = require('fixed-merkle-tree')
const { redisUrl, wsRpcUrl, minerMerkleTreeHeight, torn } = require('./config')
const { poseidonHash2 } = require('./utils')
const { toBN } = require('web3-utils')
const Redis = require('ioredis')
const redis = new Redis(redisUrl)
const ENSResolver = require('./resolver')
const resolver = new ENSResolver()
const Web3 = require('web3')
const web3 = new Web3(
new Web3.providers.WebsocketProvider(wsRpcUrl, {
clientConfig: {
maxReceivedFrameSize: 100000000,
maxReceivedMessageSize: 100000000,
},
}),
)
const MerkleTree = require('@tornado/fixed-merkle-tree')
const { minerMerkleTreeHeight, torn, netId } = require('./config')
const { poseidonHash2, toBN, logRelayerError } = require('./utils')
const resolver = require('./modules/resolver')
const web3 = require('./modules/web3')('ws')
const MinerABI = require('../abis/mining.abi.json')
const { redis } = require('./modules/redis')
let contract
// eslint-disable-next-line no-unused-vars
let tree, eventSubscription, blockSubscription
// todo handle the situation when we have two rewards in one block
async function fetchEvents(from = 0, to = 'latest') {
try {
const events = await contract.getPastEvents('NewAccount', {
fromBlock: from,
toBlock: to,
})
return events
.sort((a, b) => a.returnValues.index - b.returnValues.index)
.map(e => toBN(e.returnValues.commitment))
} catch (e) {
console.error('error fetching events', e)
async function fetchEvents(fromBlock, toBlock) {
if (fromBlock <= toBlock) {
try {
return await contract.getPastEvents('NewAccount', {
fromBlock,
toBlock,
})
} catch (error) {
const midBlock = (fromBlock + toBlock) >> 1
if (midBlock - fromBlock < 2) {
throw new Error(`error fetching events: ${error.message}`)
}
const arr1 = await fetchEvents(fromBlock, midBlock)
const arr2 = await fetchEvents(midBlock + 1, toBlock)
return [...arr1, ...arr2]
}
}
return []
}
async function processNewEvent(err, event) {
@@ -105,14 +101,29 @@ async function init() {
console.log('Initializing')
const miner = await resolver.resolve(torn.miningV2.address)
contract = new web3.eth.Contract(MinerABI, miner)
const block = await web3.eth.getBlockNumber()
const events = await fetchEvents(0, block)
tree = new MerkleTree(minerMerkleTreeHeight, events, { hashFunction: poseidonHash2 })
const cachedEvents = require(`../cache/accounts_farmer_${netId}.json`)
const cachedCommitments = cachedEvents.map(e => toBN(e.commitment))
const toBlock = await web3.eth.getBlockNumber()
const [{ blockNumber: fromBlock }] = cachedEvents.slice(-1)
const newEvents = await fetchEvents(fromBlock + 1, toBlock)
const newCommitments = newEvents
.sort((a, b) => a.returnValues.index - b.returnValues.index)
.map(e => toBN(e.returnValues.commitment))
.filter((item, index, arr) => !index || item !== arr[index - 1])
const commitments = cachedCommitments.concat(newCommitments)
tree = new MerkleTree(minerMerkleTreeHeight, commitments, { hashFunction: poseidonHash2 })
await updateRedis()
console.log(`Rebuilt tree with ${events.length} elements, root: ${tree.root()}`)
eventSubscription = contract.events.NewAccount({ fromBlock: block + 1 }, processNewEvent)
console.log(`Rebuilt tree with ${commitments.length} elements, root: ${tree.root()}`)
eventSubscription = contract.events.NewAccount({ fromBlock: toBlock + 1 }, processNewEvent)
blockSubscription = web3.eth.subscribe('newBlockHeaders', processNewBlock)
} catch (e) {
await logRelayerError(redis, e)
console.error('error on init treeWatcher', e.message)
}
}

View File

@@ -1,28 +1,30 @@
const { instances, netId } = require('./config')
const { poseidon } = require('circomlib')
const { toBN, toChecksumAddress, BN } = require('web3-utils')
const { poseidon } = require('@tornado/circomlib')
const { toBN, toChecksumAddress, BN, fromWei, isAddress, toWei, toHex } = require('web3-utils')
const TOKENS = {
torn: {
tokenAddress: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
symbol: 'TORN',
decimals: 18,
},
const addressMap = new Map()
const instance = instances[netId]
for (const [currency, { instanceAddress, symbol, decimals }] of Object.entries(instance)) {
Object.entries(instanceAddress).forEach(([amount, address]) =>
addressMap.set(`${netId}_${address}`, {
currency,
amount,
symbol,
decimals,
}),
)
}
const sleep = ms => new Promise(res => setTimeout(res, ms))
function getInstance(address) {
address = toChecksumAddress(address)
const inst = instances[`netId${netId}`]
for (const currency of Object.keys(inst)) {
for (const amount of Object.keys(inst[currency].instanceAddress)) {
if (inst[currency].instanceAddress[amount] === address) {
return { currency, amount }
}
}
const key = `${netId}_${toChecksumAddress(address)}`
if (addressMap.has(key)) {
return addressMap.get(key)
} else {
throw new Error('Unknown contact address')
}
return null
}
const poseidonHash = items => toBN(poseidon(items).toString())
@@ -51,24 +53,6 @@ function when(source, event) {
})
}
function getArgsForOracle() {
const tokens = {
...instances.netId1,
...TOKENS,
}
const tokenAddresses = []
const oneUintAmount = []
const currencyLookup = {}
Object.entries(tokens).map(([currency, data]) => {
if (currency !== 'eth') {
tokenAddresses.push(data.tokenAddress)
oneUintAmount.push(toBN('10').pow(toBN(data.decimals.toString())).toString())
currencyLookup[data.tokenAddress] = currency
}
})
return { tokenAddresses, oneUintAmount, currencyLookup }
}
function fromDecimals(value, decimals) {
value = value.toString()
let ether = value.toString()
@@ -118,12 +102,42 @@ function fromDecimals(value, decimals) {
return new BN(wei.toString(10), 10)
}
class RelayerError extends Error {
constructor(message, score = 0) {
super(message)
this.score = score
}
}
const logRelayerError = async (redis, e) => {
await redis.zadd('errors', 'INCR', e.score || 1, e.message)
}
const readRelayerErrors = async redis => {
const set = await redis.zrevrange('errors', 0, -1, 'WITHSCORES')
const errors = []
while (set.length) {
const [message, score] = set.splice(0, 2)
errors.push({ message, score })
}
return errors
}
module.exports = {
getInstance,
setSafeInterval,
poseidonHash2,
sleep,
when,
getArgsForOracle,
fromDecimals,
toBN,
toChecksumAddress,
fromWei,
toWei,
toHex,
BN,
isAddress,
RelayerError,
logRelayerError,
readRelayerErrors,
}

View File

@@ -1,34 +1,40 @@
const fs = require('fs')
const Web3 = require('web3')
const { toBN, toWei, fromWei, toChecksumAddress } = require('web3-utils')
const MerkleTree = require('fixed-merkle-tree')
const Redis = require('ioredis')
const { GasPriceOracle } = require('gas-price-oracle')
const { Utils, Controller } = require('tornado-anonymity-mining')
const MerkleTree = require('@tornado/fixed-merkle-tree')
const { Utils, Controller } = require('@tornado/anonymity-mining')
const { TornadoFeeOracleV5 } = require('@tornado/tornado-oracles')
const swapABI = require('../abis/swap.abi.json')
const miningABI = require('../abis/mining.abi.json')
const tornadoABI = require('../abis/tornadoABI.json')
const tornadoProxyABI = require('../abis/tornadoProxyABI.json')
const { queue } = require('./queue')
const { poseidonHash2, getInstance, fromDecimals, sleep } = require('./utils')
const {
poseidonHash2,
getInstance,
sleep,
toBN,
fromWei,
toChecksumAddress,
isAddress,
RelayerError,
logRelayerError,
} = require('./utils')
const { jobType, status } = require('./constants')
const {
torn,
netId,
redisUrl,
gasLimits,
instances,
privateKey,
httpRpcUrl,
oracleRpcUrl,
baseFeeReserve,
miningServiceFee,
tornadoServiceFee,
tornadoGoerliProxy,
} = require('./config')
const ENSResolver = require('./resolver')
const resolver = new ENSResolver()
const { TxManager } = require('tx-manager')
const resolver = require('./modules/resolver')
const { TxManager } = require('@tornado/tx-manager')
const { redis, redisSubscribe } = require('./modules/redis')
const getWeb3 = require('./modules/web3')
let web3
let currentTx
@@ -38,9 +44,7 @@ let txManager
let controller
let swap
let minerContract
const redis = new Redis(redisUrl)
const redisSubscribe = new Redis(redisUrl)
const gasPriceOracle = new GasPriceOracle({ defaultRpc: oracleRpcUrl })
const feeOracle = new TornadoFeeOracleV5(netId, oracleRpcUrl)
async function fetchTree() {
const elements = await redis.get('tree:elements')
@@ -74,13 +78,20 @@ async function fetchTree() {
async function start() {
try {
web3 = new Web3(httpRpcUrl)
await clearErrors()
web3 = getWeb3()
const { CONFIRMATIONS, MAX_GAS_PRICE } = process.env
txManager = new TxManager({
privateKey,
rpcUrl: httpRpcUrl,
config: { CONFIRMATIONS, MAX_GAS_PRICE, THROW_ON_REVERT: false },
config: {
CONFIRMATIONS,
MAX_GAS_PRICE,
THROW_ON_REVERT: false,
BASE_FEE_RESERVE_PERCENTAGE: baseFeeReserve,
},
})
swap = new web3.eth.Contract(swapABI, await resolver.resolve(torn.rewardSwap.address))
minerContract = new web3.eth.Contract(miningABI, await resolver.resolve(torn.miningV2.address))
redisSubscribe.subscribe('treeUpdate', fetchTree)
@@ -94,61 +105,57 @@ async function start() {
queue.process(processJob)
console.log('Worker started')
} catch (e) {
await logRelayerError(redis, e)
console.error('error on start worker', e.message)
}
}
function checkFee({ data }) {
function checkFee({ data }, gasInfo) {
if (data.type === jobType.TORNADO_WITHDRAW) {
return checkTornadoFee(data)
return checkTornadoFee(data, gasInfo)
}
return checkMiningFee(data)
}
async function checkTornadoFee({ args, contract }) {
const { currency, amount } = getInstance(contract)
const { decimals } = instances[`netId${netId}`][currency]
const [fee, refund] = [args[4], args[5]].map(toBN)
const { fast } = await gasPriceOracle.gasPrices()
async function checkTornadoFee({ args, contract }, tx) {
const { currency, amount, decimals } = getInstance(contract)
const [userProvidedFee, refund] = [args[4], args[5]].map(toBN)
const { gasLimit, gasPrice } = tx
const ethPrice = await redis.hget('prices', currency)
const expense = toBN(toWei(fast.toString(), 'gwei')).mul(toBN(gasLimits[jobType.TORNADO_WITHDRAW]))
const feePercent = toBN(fromDecimals(amount, decimals))
.mul(toBN(parseInt(tornadoServiceFee * 1e10)))
.div(toBN(1e10 * 100))
let desiredFee
switch (currency) {
case 'eth': {
desiredFee = expense.add(feePercent)
break
}
default: {
desiredFee = expense
.add(refund)
.mul(toBN(10 ** decimals))
.div(toBN(ethPrice))
desiredFee = desiredFee.add(feePercent)
break
}
}
const totalWithdrawalFee = await feeOracle.calculateWithdrawalFeeViaRelayer({
tx,
txType: 'relayer_withdrawal',
amount,
currency,
decimals,
refundInEth: refund.toString(),
predefinedGasLimit: gasLimit,
predefinedGasPrice: gasPrice,
tokenPriceInEth: ethPrice,
relayerFeePercent: tornadoServiceFee,
})
console.log(
'sent fee, desired fee, feePercent',
fromWei(fee.toString()),
fromWei(desiredFee.toString()),
fromWei(feePercent.toString()),
'user-provided fee, desired fee',
fromWei(userProvidedFee.toString()),
fromWei(toBN(totalWithdrawalFee).toString()),
)
if (fee.lt(desiredFee)) {
throw new Error('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.')
if (userProvidedFee.lt(toBN(totalWithdrawalFee))) {
throw new RelayerError(
'Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.',
0,
)
}
}
async function checkMiningFee({ args }) {
const { fast } = await gasPriceOracle.gasPrices()
const gasPrice = await feeOracle.getGasPrice()
const ethPrice = await redis.hget('prices', 'torn')
const isMiningReward = currentJob.data.type === jobType.MINING_REWARD
const providedFee = isMiningReward ? toBN(args.fee) : toBN(args.extData.fee)
const expense = toBN(toWei(fast.toString(), 'gwei')).mul(toBN(gasLimits[currentJob.data.type]))
const expense = toBN(gasPrice).mul(toBN(gasLimits[currentJob.data.type]))
const expenseInTorn = expense.mul(toBN(1e18)).div(toBN(ethPrice))
// todo make aggregator for ethPrices and rewardSwap data
const balance = await swap.methods.tornVirtualBalance().call()
@@ -170,19 +177,17 @@ async function checkMiningFee({ args }) {
serviceFeePercent.toString(),
)
if (toBN(providedFee).lt(desiredFee)) {
throw new Error('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.')
throw new RelayerError('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.')
}
}
async function getProxyContract() {
let proxyAddress
if (netId === 5) {
proxyAddress = tornadoGoerliProxy
} else {
proxyAddress = await resolver.resolve(torn.tornadoProxy.address)
proxyAddress = await resolver.resolve(torn.tornadoRouter.address)
}
const contract = new web3.eth.Contract(tornadoProxyABI, proxyAddress)
return {
@@ -196,6 +201,18 @@ function checkOldProxy(address) {
return toChecksumAddress(address) === toChecksumAddress(OLD_PROXY)
}
async function checkRecipient({ data }) {
// Checks only for default withdrawals
if (data.type !== jobType.TORNADO_WITHDRAW) return
console.log(data.args)
const recipient = data.args[2]
if (!isAddress(recipient)) throw new Error('Recipient address is invalid')
const addressCode = await web3.eth.getCode(toChecksumAddress(recipient))
if (addressCode !== '0x') throw new Error('Recipient cannot be a smart-contract, only EOA')
}
async function getTxObject({ data }) {
if (data.type === jobType.TORNADO_WITHDRAW) {
let { contract, isOldProxy } = await getProxyContract()
@@ -207,12 +224,18 @@ async function getTxObject({ data }) {
calldata = contract.methods.withdraw(data.proof, ...data.args).encodeABI()
}
return {
const incompleteTx = {
value: data.args[5],
from: txManager.address, // Required, because without it relayerRegistry.burn will fail, because msg.sender is not relayer
to: contract._address,
data: calldata,
gasLimit: gasLimits['WITHDRAW_WITH_EXTRA'],
}
const { gasLimit, gasPrice } = await feeOracle.getGasParams({
tx: incompleteTx,
txType: 'relayer_withdrawal',
})
return { ...incompleteTx, gasLimit, gasPrice }
} else {
const method = data.type === jobType.MINING_REWARD ? 'reward' : 'withdraw'
const calldata = minerContract.methods[method](data.proof, data.args).encodeABI()
@@ -241,7 +264,7 @@ async function isOutdatedTreeRevert(receipt, currentTx) {
async function processJob(job) {
try {
if (!jobType[job.data.type]) {
throw new Error(`Unknown job type: ${job.data.type}`)
throw new RelayerError(`Unknown job type: ${job.data.type}`)
}
currentJob = job
await updateStatus(status.ACCEPTED)
@@ -250,13 +273,15 @@ async function processJob(job) {
} catch (e) {
console.error('processJob', e.message)
await updateStatus(status.FAILED)
throw e
throw new RelayerError(e.message)
}
}
async function submitTx(job, retry = 0) {
await checkFee(job)
currentTx = await txManager.createTx(await getTxObject(job))
await checkRecipient(job)
const rawTx = await getTxObject(job)
await checkFee(job, rawTx)
currentTx = await txManager.createTx(rawTx)
if (job.data.type !== jobType.TORNADO_WITHDRAW) {
await fetchTree()
@@ -283,10 +308,10 @@ async function submitTx(job, retry = 0) {
await updateStatus(status.RESUBMITTED)
await submitTx(job, retry + 1)
} else {
throw new Error('Tree update retry limit exceeded')
throw new RelayerError('Tree update retry limit exceeded')
}
} else {
throw new Error('Submitted transaction failed')
throw new RelayerError('Submitted transaction failed')
}
}
} catch (e) {
@@ -302,10 +327,10 @@ async function submitTx(job, retry = 0) {
console.log('Tree is still not up to date, resubmitting')
await submitTx(job, retry + 1)
} else {
throw new Error('Tree update retry limit exceeded')
throw new RelayerError('Tree update retry limit exceeded')
}
} else {
throw new Error(`Revert by smart contract ${e.message}`)
throw new RelayerError(`Revert by smart contract ${e.message}`)
}
}
}
@@ -328,4 +353,9 @@ async function updateStatus(status) {
await currentJob.update(currentJob.data)
}
async function clearErrors() {
console.log('Errors list cleared')
await redis.del('errors')
}
start()

View File

@@ -4,7 +4,7 @@ const {
getTornadoWithdrawInputError,
getMiningRewardInputError,
getMiningWithdrawInputError,
} = require('../src/validator')
} = require('../src/modules/validator')
describe('Validator', () => {
describe('#getTornadoWithdrawInputError', () => {
@@ -19,7 +19,13 @@ describe('Validator', () => {
'.proof should match pattern "^0x[a-fA-F0-9]{512}$"',
)
})
it('should throw if unknown contract', () => {
const malformedData = { ...withdrawData }
malformedData.contract = '0xf17f52151ebef6c7334fad080c5704d77216b732'
getTornadoWithdrawInputError(malformedData).should.be.equal(
'.contract should pass "isKnownContract" keyword validation',
)
})
it('should throw something is missing', () => {
const malformedData = { ...withdrawData }
delete malformedData.proof

15
tornado-stream.conf Normal file
View File

@@ -0,0 +1,15 @@
map $ssl_preread_server_name $name {
yourdomain.com tornado_mainnet;
default tornado_mainnet;
}
upstream tornado_mainnet {
server 127.0.0.1:4380;
}
server {
listen 0.0.0.0:443;
proxy_pass $name;
ssl_preread on;
}

87
tornado.conf Normal file
View File

@@ -0,0 +1,87 @@
# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
# scheme used to connect to this server
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
default $http_x_forwarded_proto;
'' $scheme;
}
# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
# server port the client connected to
map $http_x_forwarded_port $proxy_x_forwarded_port {
default $http_x_forwarded_port;
'' $server_port;
}
# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
# Connection header that may have been passed to this server
map $http_upgrade $proxy_connection {
default upgrade;
'' close;
}
# Apply fix for very long server names
server_names_hash_bucket_size 128;
# Default dhparam
# Set appropriate X-Forwarded-Ssl header based on $proxy_x_forwarded_proto
map $proxy_x_forwarded_proto $proxy_x_forwarded_ssl {
default off;
https on;
}
gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
log_format vhost '$host $remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'"$upstream_addr"';
# HTTP 1.1 support
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
proxy_set_header X-Original-URI $request_uri;
# Mitigate httpoxy attack (see README for details)
proxy_set_header Proxy "";
# Request rate limiting per second, 2Mb zone @ 5 requests per second
limit_req_zone $binary_remote_addr zone=one:2m rate=5r/s;
# Connections per IP limited to 2
limit_conn_zone $binary_remote_addr zone=two:2m;
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
server_tokens off;
listen 80;
access_log /var/log/nginx/access.log vhost;
return 503;
}
server {
server_name yourdomain.com;
# Connection timeouts
client_body_timeout 10s;
client_header_timeout 10s;
listen 80;
access_log /var/log/nginx/access.log vhost;
# Do not HTTPS redirect LetsEncrypt ACME challenge
location ^~ /.well-known/acme-challenge/ {
limit_req zone=one;
limit_conn two 1;
proxy_pass http://127.0.0.1:8000;
break;
}
location / {
limit_req zone=one;
limit_conn two 1;
return 301 https://$host$request_uri;
}
}

4939
yarn.lock

File diff suppressed because it is too large Load Diff