From 9d6c6144bf94db9af3affafa2d52d0a219c0cbcd Mon Sep 17 00:00:00 2001 From: Theo Date: Thu, 5 Oct 2023 12:23:22 -0700 Subject: [PATCH] Add script to pin all actual Tornado Cash IPFS hashes on any debian server --- guide/SOURCES.html | 680 -------------------------------------------- guide/SOURCES.md | 30 -- scripts/hostIPFS.sh | 128 +++++++++ scripts/pinIPFS.ts | 15 + src/contstants.ts | 4 +- src/downloader.ts | 8 +- src/types.ts | 4 +- 7 files changed, 152 insertions(+), 717 deletions(-) delete mode 100644 guide/SOURCES.html delete mode 100644 guide/SOURCES.md create mode 100644 scripts/hostIPFS.sh create mode 100644 scripts/pinIPFS.ts diff --git a/guide/SOURCES.html b/guide/SOURCES.html deleted file mode 100644 index 123cfa8..0000000 --- a/guide/SOURCES.html +++ /dev/null @@ -1,680 +0,0 @@ - - - - - -SOURCES - -
-

Tornado Cash decentralized sources

This is an entrypoint page to source code and minified versions of all Tornado Cash services and packages.

Basic users guide

If you want to use trusted local versions of Tornado sites, you can download it directly from IPFS:

  1. Classic UI (main site) - classic-ui.minified.sources.tornadocash.eth, actual IPFS hash - bafybeia4x75bn74prpmi6jqa6gbgmsfovunnke5s6oyrddxyopnz6kw5le

  2. Nova UI - nova.minified.sources.tornadocash.eth, actual IPFS hash - bafybeihr5h2tfonjn2gybd4yhvchdwt4eyo25lf5twhhn6bnasdv2wwz7i

  3. Tornado CLI - cli.minified.sources.tornadocash.eth, actual IPFS hash - bafybeicxxyqjgx5ggvujij4fnjdcwkep4nj5662yrxftb7y3pzwl77qntq

Just click on contenthash field by link or copy IPFS hash and paste in any IPFS gateway, and then file start downloading.

Developers guide

Download all Tornado Cash git repositories is easy via special tool located on download.sources.tornadocash.eth.

Clone repo with tool via any IPFS resolver by CID on ENS domain above and follow tool readme (located in repo), for example:

or

 

- - \ No newline at end of file diff --git a/guide/SOURCES.md b/guide/SOURCES.md deleted file mode 100644 index 186966b..0000000 --- a/guide/SOURCES.md +++ /dev/null @@ -1,30 +0,0 @@ -# Tornado Cash decentralized sources - -This is an entrypoint page to source code and minified versions of all Tornado Cash services and packages. - -### Basic users guide - -If you want to use trusted local versions of Tornado sites, you can download it directly from IPFS: - -1. Classic UI (main site) - [classic-ui.minified.sources.tornadocash.eth](https://app.ens.domains/classic-ui.minified.sources.tornadocash.eth), actual IPFS hash - `bafybeia4x75bn74prpmi6jqa6gbgmsfovunnke5s6oyrddxyopnz6kw5le` -2. Nova UI - [nova.minified.sources.tornadocash.eth](https://app.ens.domains/nova.minified.sources.tornadocash.eth), actual IPFS hash - `bafybeihr5h2tfonjn2gybd4yhvchdwt4eyo25lf5twhhn6bnasdv2wwz7i` -3. Tornado CLI - [cli.minified.sources.tornadocash.eth](https://app.ens.domains/cli.minified.sources.tornadocash.eth), actual IPFS hash - `bafybeicxxyqjgx5ggvujij4fnjdcwkep4nj5662yrxftb7y3pzwl77qntq` - -Just click on `contenthash` field by link or copy IPFS hash and paste in [any IPFS gateway](https://ipfs.github.io/public-gateway-checker/), and then file start downloading. - -### Developers guide - -Download all Tornado Cash git repositories is easy via special tool located on [download.sources.tornadocash.eth](https://app.ens.domains/download.sources.tornadocash.eth). - -Clone repo with tool via any IPFS resolver by CID on ENS domain above and follow tool readme (located in repo), for example: - -```` -git clone https://ipfs.io/ipfs/bafybeienn6huru6cxs3ovl5ogeik2wnvviqtoainqj4zn2q3q7zzezvu6u downloader-tool -```` - -or - -``` -git clone https://cloudflare-ipfs.com/ipfs/bafybeienn6huru6cxs3ovl5ogeik2wnvviqtoainqj4zn2q3q7zzezvu6u -``` - diff --git a/scripts/hostIPFS.sh b/scripts/hostIPFS.sh new file mode 100644 index 0000000..d14ea50 --- /dev/null +++ b/scripts/hostIPFS.sh @@ -0,0 +1,128 @@ +#!/bin/bash +# Script must be running from root +if [ "$EUID" -ne 0 ]; + then echo "Please run as root"; + exit 1; +fi; + +this_repo_ipfs_cid="bafybeienn6huru6cxs3ovl5ogeik2wnvviqtoainqj4zn2q3q7zzezvu6u"; +user_home_dir=$(eval echo ~$USER); +tornado_folder="$user_home_dir/tornado-ipfs"; +script_log_file="/tmp/tornado-ipfs-installation.log"; +cron_script_path="$tornado_folder/cronfile.cron"; + +function echo_log_err(){ + echo $1 1>&2; + echo -e "$1\n" &>> $script_log_file; +} + +function echo_log_err_and_exit(){ + echo_log_err "$1"; + exit 1; +} + +function delete_if_exists(){ + if test -d $1; then rm -rf $1; fi; + if test -f $1; then rm $1; fi; +} + +function is_package_installed(){ + if [ $(dpkg-query -W -f='${Status}' $1 2>/dev/null | grep -c "ok installed") -eq 0 ]; then return 1; else return 0; fi; +} + +function install_requred_packages(){ + apt update &>> $script_log_file; + + requred_packages=("curl" "git" "ufw" "cron"); + local package; + for package in ${requred_packages[@]}; do + if ! is_package_installed $package; then + # Kill apache process, because Debian configuring nginx package right during installation + if [ $package = "nginx" ]; then systemctl stop apache2; fi; + apt install --yes --force-yes -o DPkg::Options::="--force-confold" $package &>> $script_log_file; + if ! is_package_installed $package; then + echo_log_err_and_exit "Error: cannot install \"$package\" package"; + fi; + fi; + done; + + echo -e "\nAll required packages installed successfully"; +} + +function install_node(){ + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash &>> $script_log_file; + . ~/.nvm/nvm.sh; + . ~/.profile; + . ~/.bashrc; + nvm install 16.20.2 &>> $script_log_file; +} + +function download_repo(){ + ipfs_gateways=("https://ipfs.io/ipfs" "https://dweb.link/ipfs" "https://cloudflare-ipfs.com/ipfs" "https://gateway.pinata.cloud/ipfs" "https://hardbin.com/ipfs"); + for gateway in ${ipfs_gateways[@]}; do + delete_if_exists $tornado_folder; + if git clone $gateway/$this_repo_ipfs_cid $tornado_folder; then break; fi; + done; + if [ ! -d $tornado_folder ]; then echo_log_err_and_exit "Cannot download repository from IPFS"; fi; +} + +function install_node_dependencies(){ + npm i -g yarn &>> $script_log_file; + cd $tornado_folder; + yarn &>> $script_log_file; +} + +function run_ipfs_daemon(){ + cd $tornado_folder; + wget https://dist.ipfs.tech/kubo/v0.22.0/kubo_v0.22.0_linux-amd64.tar.gz; + tar -xvzf kubo_v0.22.0_linux-amd64.tar.gz; + cd kubo; + sudo bash install.sh; + ipfs init --profile server; + sudo -b ipfs daemon; +} + +function configure_firewall(){ + ufw allow 8080; + ufw allow 4001; + ufw deny 5000; + ufw deny 5001; + ufw insert 1 allow OpenSSH; + echo "y" | ufw enable; + ufw reload; +} + +function add_to_cron(){ + delete_if_exists $cron_script_path; + + # Add startup script to cron (job sheduler) to up IPFS daemon after restart + echo "@reboot ipfs daemon &" > $cron_script_path; + + # Add existing cron rules (not related to this ipfs) to cron script, so that they are not removed + # https://unix.stackexchange.com/questions/21297/how-do-i-add-an-entry-to-my-crontab + crontab -l | grep -v "ipfs daemon" >> $cron_script_path; + + crontab $cron_script_path; + systemctl restart cron; + + if crontab -l | grep -q "ipfs daemon"; then + echo "IPFS daemon added to cron autorun successfully"; + else + echo_log_err "Warning: adding script to cron autorun failed."; + fi; +} + +function run_pinner(){ + cd $tornado_folder; + yarn pin; +} + + +install_requred_packages; +install_node; +download_repo; +install_node_dependencies; +run_ipfs_daemon; +configure_firewall; +add_to_cron; +run_pinner; \ No newline at end of file diff --git a/scripts/pinIPFS.ts b/scripts/pinIPFS.ts new file mode 100644 index 0000000..3528207 --- /dev/null +++ b/scripts/pinIPFS.ts @@ -0,0 +1,15 @@ +import { getV1Cids } from "../src/downloader.ts"; +import { mainRepos, packageRepos, sitesENS, sourcesDomain } from "../src/contstants.ts"; +import { execSync } from "child_process"; + +const ensDomains = [...Object.values(mainRepos), ...Object.values(packageRepos), ...sitesENS, sourcesDomain]; +const ipfsCids = await getV1Cids(ensDomains.reduce((acc, dom) => Object.assign(acc, { [dom]: dom }), {})); +for (const [domain, cid] of Object.entries(ipfsCids)) { + try { + execSync(`ipfs pin add ${cid}`); + } catch (e) { + console.error(`Cannot pin ipfs content by cid ${cid} for domain ${domain}`); + } +} + +console.log("IPFS cids successfully added"); diff --git a/src/contstants.ts b/src/contstants.ts index 60d1fd6..1193ba1 100644 --- a/src/contstants.ts +++ b/src/contstants.ts @@ -1,7 +1,7 @@ import "dotenv/config"; const rootTornadoDomain = process.env.ENS_ROOT_DOMAIN || "tornadocash.eth"; -const sourcesDomain = "sources." + rootTornadoDomain; +export const sourcesDomain = "sources." + rootTornadoDomain; const packagesDomain = "packages." + sourcesDomain; export const mainRepos = { @@ -30,6 +30,8 @@ export const packageRepos = { "merkle-root-updater": "merkle-root-updater." + packagesDomain, } as const; +export const sitesENS = ["tornadocash.eth", "nova.tornadocash.eth", "relayers-ui.tornadocash.eth", "docs.tornadocash.eth"] as const; + export const knownIpfsResources = [ `https://ipfs.io/ipfs/`, `https://dweb.link/ipfs/`, diff --git a/src/downloader.ts b/src/downloader.ts index a8fa82f..609b3ea 100644 --- a/src/downloader.ts +++ b/src/downloader.ts @@ -10,7 +10,7 @@ import { Domains, IPFSCids, RepoName } from "./types.ts"; const provider = new ethers.JsonRpcProvider(process.env.RPC_URL || "https://rpc.mevblocker.io"); const git: SimpleGit = simpleGit(); -async function getENSDomainContenthash(repoName: RepoName, domain: string): Promise<[RepoName, string]> { +async function getENSDomainContenthash(domain: string): Promise { const resolver = await provider.getResolver(domain); if (!resolver) throw new Error(`Resolver for domain ${domain} not found!`); @@ -19,18 +19,18 @@ async function getENSDomainContenthash(repoName: RepoName, domain: string): Prom if (!contentHash.startsWith("ipfs")) throw new Error(`Contenthash for domain ${domain} invalid, non-IPFS format: ${contentHash}`); try { - return [repoName, cidV0ToV1Base32(contentHash.replace("ipfs://", ""))]; + return cidV0ToV1Base32(contentHash.replace("ipfs://", "")); } catch (e) { throw new Error(`Contenthash for domain ${domain} is invalid, cannot convert to V1 IPFS CID: ${contentHash}`); } } export async function getV1Cids(domains: Domains): Promise { - const promises = Object.entries(domains).map(([repoName, domain]) => getENSDomainContenthash(repoName as RepoName, domain)); + const promises = Object.entries(domains).map(([repoName, domain]) => (async () => [repoName, await getENSDomainContenthash(domain)])()); const result = await Promise.allSettled(promises); const ipfsCids = result.reduce((acc, res) => { if (res.status === "rejected") console.error(res.reason); - else acc[res.value[0]] = res.value[1]; + else acc[res.value[0] as RepoName] = res.value[1]; return acc; }, {} as IPFSCids); diff --git a/src/types.ts b/src/types.ts index c68d107..c686298 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,6 @@ -import { mainRepos, packageRepos } from "./contstants.ts"; +import { mainRepos, packageRepos, sitesENS } from "./contstants.ts"; -export type RepoName = keyof typeof mainRepos | keyof typeof packageRepos; +export type RepoName = keyof typeof mainRepos | keyof typeof packageRepos | (typeof sitesENS)[number]; export type Domains = Partial>; export type IPFSCids = Record;