Proposal #65: Update ETH RPC List and ENS
Reason: The current ETH RPC List on the UI no longer support historical queries, necessitating an update to the default ETH RPC list.
UI Update Details:
ETH RPCs: blockscoutRPC,blastRPC,xrpc, gasHawkRPC, lavaRPC,torndaoRPC,sentioRPC,tornadoRPC
Target: Update the IPFS hash for the Tornado Cash classic UI associated with the ENS domain tornadocash.eth.
UI Code Repository: https://codeberg.org/torndao/classic-ui/commits/branch/development
UI Code commit: https://codeberg.org/torndao/classic-ui/commit/d52c01688f5697e60a1d3c598a4c30989403fb0f
IPFS Hash: bafybeie5hxovqc4ifcnrnhvmjbefxgeix6oqvzaspyytdxiyscji22v5pu (accessible via https://ipfs.io/ipfs/bafybeie5hxovqc4ifcnrnhvmjbefxgeix6oqvzaspyytdxiyscji22v5pu)
IPFS Hash Verification Tool: https://codeberg.org/torndao/tornado-ipfs-ui
Content Hash: e301017012209d3ddd580b88289b169eac48485b9888bf9d0ae4127e3131dd1890928d6abd7d (calculated using the tool at https://codeberg.org/torndao/tornado-ipfs-ui/src/branch/main/ContentHash.html)
Affected DomainsThe updated IPFS hash will affect the following domains:
tornadocash.eth.limo
tornadocash.eth.link
ipfs.io/ipns/tornadocash.eth
tornadocash-eth.ipns.dweb.linkThis proposal passed with 330.32k TORN (72%) voting For and 125.59k TORN (28%) voting Against. The required quorum of 100k TORN was met, and the proposal was successfully executed here (Feb-14-2026 12:20:23 PM UTC).
- Code repository: https://codeberg.org/torndao/classic-ui/commits/branch/development
- UI code commit version: https://codeberg.org/torndao/classic-ui/commit/d52c01688f5697e60a1d3c598a4c30989403fb0f
There are three new commits compared to the last version (https://tornadocash.eth.limo/governance/63; https://codeberg.org/torndao/classic-ui/commit/30c4b44a5cd96811e17ef46d921ea8e1841a05d0; https://ipfs.io/ipfs/bafybeiadcezfjx4ewel2xbdic66a4oj5ei6wtrvcbnirao5543cqtckukm):
- https://codeberg.org/torndao/classic-ui/commit/6ed828199d8a9eb4fedec2764712af81bd13a7e5 (commit message: "Update the @tornado library file URL."),
- https://codeberg.org/torndao/classic-ui/commit/25c5a6fc9801706d3fb9f009f3e470b24b5bb2b9 (commit message: "update event files"),
- https://codeberg.org/torndao/classic-ui/commit/d52c01688f5697e60a1d3c598a4c30989403fb0f (commit message: "Update Ethereum RPCs").
- Commit
6ed828199d8a9eb4fedec2764712af81bd13a7e5(commit message: "Update the @tornado library file URL.") is the most dangerous one since it changes thenpmregistry link to@tornado:registry=https://codeberg.org/api/packages/torndao/npm/(see https://codeberg.org/torndao/-/packages) and also updates theyarn.lockfile.
The SHA-1 and SHA-512 hashes have been asserted via this Bash script:
#!/usr/bin/env bash
set -Eeuo pipefail
# See https://codeberg.org/torndao/classic-ui/commit/6ed828199d8a9eb4fedec2764712af81bd13a7e5.
declare -A sha1_expected=(
["fixed-merkle-tree"]="dfcb5e870513e3d0b9300a5c3cb471b7dbddba96"
["gas-price-oracle"]="2bfecf70437d22e33e8912a04d9e0149bf7b67de"
["snarkjs"]="715aaf30248fffb9b7a1ee558e9d1b9a765a0e99"
["tornado-config"]="0ab73fae5d6b95712396479911c6da9a6b407c89"
["tornado-oracles"]="6a1766e0561a322b0be224f5e15fb085b30d5a7c"
["websnark"]="c15196db16e8c5965bd7c9938692b4289fd4e284"
)
# See https://codeberg.org/torndao/classic-ui/commit/6ed828199d8a9eb4fedec2764712af81bd13a7e5.
declare -A sha512_expected=(
["fixed-merkle-tree"]="IZ+NG1yIV9TPApuaqSzylcZn2ZYqBoG67hSVTif4dagsF4pvCF9NaM3YUrZPy1UdpJp4xps08DXlUuF/xlLIeg=="
["gas-price-oracle"]="fHGLKxSMEWY0LtMj8ErRX4NcqTueryYPEmmNzJx+vNydAwx5ecjBQSV4zQPRNPrJdokn5WhLb/1SFGOBqHaSuA=="
["snarkjs"]="Df0QvExjephUuWYWyXeIEZI91KZv+bBtIk/1aCWNXtglYl7ZnoeU3/BPvfR2JKRLR/WdxcFTXZSdKaIR10JlQg=="
["tornado-config"]="HRcZtzKhVOhsotiuguGe0UMb/30nqFweRw+3PmnULLxKrXKV6zH9V1jj2R+6TBAeXFJwr8Yhe16BdHvG2/7SRg=="
["tornado-oracles"]="m2m7+I+cpxCeGx2drq/IHuuzC6TsZKmagkaX2t4MDQaGr7xu367FDkpMhex1wvqPxrJSSqsp6gEVOQBvRxwR6Q=="
["websnark"]="TZ2xanChs6CWCze2VfwlPbTbExdEblHZN8qKAXKAF1F5wkKfh4AZAUN+BnkSIWhc1eXJV/YPU74tQfgmFt5v7g=="
)
declare -A versions=(
["fixed-merkle-tree"]="0.7.3"
["gas-price-oracle"]="0.5.3"
["snarkjs"]="0.1.20"
["tornado-config"]="2.0.0"
["tornado-oracles"]="2.1.0"
["websnark"]="0.0.4"
)
for pkg in "${!versions[@]}"; do
ver=${versions[$pkg]}
url="https://codeberg.org/api/packages/torndao/npm/@tornado%2F$pkg/-/$pkg-$ver.tgz"
echo "Checking $pkg@$ver..."
# Download in memory and compute hashes.
sha1_actual=$(curl -sL "$url" | sha1sum | awk '{print $1}')
sha512_actual=$(curl -sL "$url" | openssl dgst -sha512 -binary | openssl base64 -A)
# Assert the SHA-1 hashes.
if [ "$sha1_actual" == "${sha1_expected[$pkg]}" ]; then
echo " SHA-1 checks out"
else
echo " SHA-1 expected ${sha1_expected[$pkg]}, got $sha1_actual"
fi
# Assert the SHA-512 hashes.
if [ "$sha512_actual" == "${sha512_expected[$pkg]}" ]; then
echo " SHA-512 checks out"
else
echo " SHA-512 expected ${sha512_expected[$pkg]}, got $sha512_actual"
fi
echo
doneLet's look at the diffs of the different packages.
package-fixed-merkle-tree-old:
npm pack @tornado/fixed-merkle-tree@0.7.3 --registry=https://git.tornado.ws/api/packages/tornado-packages/npm/package-fixed-merkle-tree-new:
npm pack @tornado/fixed-merkle-tree@0.7.3 --registry=https://codeberg.org/api/packages/torndao/npm/diff -r -w -B package-fixed-merkle-tree-old package-fixed-merkle-tree-new --exclude="*.d.ts"diff -r -w -B package-fixed-merkle-tree-old/package.json package-fixed-merkle-tree-new/package.json
5c5
< "repository": "https://git.tornado.ws/tornado-packages/fixed-merkle-tree.git",
---
> "repository": "https://codeberg.org/torndao/fixed-merkle-tree.git",=> The diff looks safe. I excluded the .d.ts files from the comparison because they are compile-time type artifacts and cannot introduce runtime behaviour or supply-chain payloads.
package-gas-price-oracle-old:
npm pack @tornado/gas-price-oracle@0.5.3 --registry=https://git.tornado.ws/api/packages/tornado-packages/npm/package-gas-price-oracle-new:
npm pack @tornado/gas-price-oracle@0.5.3 --registry=https://codeberg.org/api/packages/torndao/npm/diff -r -w -B package-tornado-gas-price-oracle-old package-tornado-gas-price-oracle-new --exclude="*.d.ts"Only in package-tornado-gas-price-oracle-new/lib: esm
diff -r -w -B '--exclude=*.d.ts' package-tornado-gas-price-oracle-old/lib/services/cacher/cacheNode.js package-tornado-gas-price-oracle-new/lib/services/cacher/cacheNode.js
17c17
< while (g && (g = 0, op[0] && (_ = 0)), _) try {
---
> while (_) try {
diff -r -w -B '--exclude=*.d.ts' package-tornado-gas-price-oracle-old/lib/services/gas-estimation/eip1559.js package-tornado-gas-price-oracle-new/lib/services/gas-estimation/eip1559.js
28c28
< while (g && (g = 0, op[0] && (_ = 0)), _) try {
---
> while (_) try {
diff -r -w -B '--exclude=*.d.ts' package-tornado-gas-price-oracle-old/lib/services/gas-price-oracle/gas-price-oracle.js package-tornado-gas-price-oracle-new/lib/services/gas-price-oracle/gas-price-oracle.js
28c28
< while (g && (g = 0, op[0] && (_ = 0)), _) try {
---
> while (_) try {
diff -r -w -B '--exclude=*.d.ts' package-tornado-gas-price-oracle-old/lib/services/legacy-gas-price/legacy.js package-tornado-gas-price-oracle-new/lib/services/legacy-gas-price/legacy.js
28c28
< while (g && (g = 0, op[0] && (_ = 0)), _) try {
---
> while (_) try {
diff -r -w -B '--exclude=*.d.ts' package-tornado-gas-price-oracle-old/lib/services/rpcFetcher/fetcher.js package-tornado-gas-price-oracle-new/lib/services/rpcFetcher/fetcher.js
17c17
< while (g && (g = 0, op[0] && (_ = 0)), _) try {
---
> while (_) try {
diff -r -w -B '--exclude=*.d.ts' package-tornado-gas-price-oracle-old/package.json package-tornado-gas-price-oracle-new/package.json
5c5
< "homepage": "https://git.tornado.ws/tornado-packages/gas-price-oracle",
---
> "homepage": "https://codeberg.org/torndao/gas-price-oracle",
11c11
< "url": "https://git.tornado.ws/tornado-packages/gas-price-oracle"
---
> "url": "https://codeberg.org/torndao/gas-price-oracle"
21,22c21
< "prepare": "yarn build && yarn build:esm",
< "prepublishOnly": "yarn test && yarn lint"
---
> "prepare": "yarn build && yarn build:esm"=> The diff looks safe because the changes are purely compiler-output and metadata updates, with no runtime or behavioural modifications. I excluded the .d.ts files from the comparison because they are compile-time type artifacts and cannot introduce runtime behaviour or supply-chain payloads.
package-snarkjs-old:
npm pack @tornado/snarkjs@0.1.20 --registry=https://git.tornado.ws/api/packages/tornado-packages/npm/package-snarkjs-new:
npm pack @tornado/snarkjs@0.1.20 --registry=https://codeberg.org/api/packages/torndao/npm/diff -r -w -B package-tornado-snarkjs-old package-tornado-snarkjs-newdiff -r -w -B package-tornado-snarkjs-old/package.json package-tornado-snarkjs-new/package.json
28c28
< "url": "https://git.tornado.ws/tornado-packages/snarkjs"
---
> "url": "https://codeberg.org/torndao/snarkjs"=> The diff looks safe.
package-tornado-config-old:
npm pack @tornado/tornado-config@2.0.0 --registry=https://git.tornado.ws/api/packages/tornado-packages/npm/package-tornado-config-new:
npm pack @tornado/tornado-config@2.0.0 --registry=https://codeberg.org/api/packages/torndao/npm/diff -r -w -B package-tornado-config-old package-tornado-config-new --exclude="*.map"diff -r -w -B package-tornado-config-old package-tornado-config-new --exclude="*.map"
diff -r -w -B '--exclude=*.map' package-tornado-config-old/lib/config.js package-tornado-config-new/lib/config.js
8c8
< pausePeriod: 45 * 24 * 3600,
---
> pausePeriod: 45 * 24 * 3600, // 45 days
diff -r -w -B '--exclude=*.map' package-tornado-config-old/package.json package-tornado-config-new/package.json
11c11
< "url": "https://git.tornado.ws/tornado-packages/tornado-config.git"
---
> "url": "https://codeberg.org/torndao/tornado-config.git"=> The diff looks safe. I excluded the .map file from the comparison because sourcemaps are non-executable debugging artifacts and do not affect runtime behaviour.
package-tornado-oracles-old:
npm pack @tornado/tornado-oracles@2.1.0 --registry=https://git.tornado.ws/api/packages/tornado-packages/npm/package-tornado-oracles-new:
npm pack @tornado/tornado-config@2.0.0 --registry=https://codeberg.org/api/packages/torndao/npm/diff -r -w -B package-tornado-oracles-old package-tornado-oracles-newdiff -r -w -B package-tornado-oracles-old/package.json package-tornado-oracles-new/package.json
18c18
< "url": "https://git.tornado.ws/tornado-packages/tornado-oracles.git"
---
> "url": "https://codeberg.org/torndao/tornado-oracles.git"
47,50c47
< ],
< "publishConfig": {
< "registry": "https://git.tornado.ws/api/packages/tornado-packages/npm"
< }
---=> The diff looks safe.
package-tornado-websnark-old:
npm pack @tornado/websnark@0.0.4 --registry=https://git.tornado.ws/api/packages/tornado-packages/npm/package-tornado-websnark-new:
npm pack @tornado/websnark@0.0.4 --registry=https://codeberg.org/api/packages/torndao/npm/diff -r -w -B package-tornado-websnark-old package-tornado-websnark-newdiff -r -w -B package-tornado-websnark-old/package.json package-tornado-websnark-new/package.json
25c25
< "url": "https://git.tornado.ws/tornado-packages/websnark"
---
> "url": "https://codeberg.org/torndao/websnark"=> The diff looks safe.
- Commit
25c5a6fc9801706d3fb9f009f3e470b24b5bb2b9(commit message: "update event files") looks fine; all files are validJSONand no suspicious patterns were found in any file. I used the following Bash command to check for any suspicious patterns in theeventsdirectory:
find . \( -name "*.json.gz" -o -name "*.json" \) -exec python3 -c "
import sys,json,zlib,re
f=sys.argv[1]
print('---',f,'---')
try:
data=open(f,'rb').read()
try:
text=zlib.decompress(data).decode('utf-8')
except:
text=data.decode('utf-8')
obj=json.loads(text)
print('[OK] Valid JSON')
allowed={'blockNumber','transactionHash','commitment','leafIndex','timestamp','nullifierHash','to','fee','logIndex','from','txHash','encryptedNote'}
bad_fields=[k for o in (obj if isinstance(obj,list) else [obj]) for k in (o.keys() if isinstance(o,dict) else []) if k not in allowed]
bad_vals=[v for o in (obj if isinstance(obj,list) else [obj]) for v in (o.values() if isinstance(o,dict) else []) if isinstance(v,str) and not (re.match(r'^(0x)?[0-9a-fA-F]+$',v) or re.match(r'^[0-9]+$',v))]
print('[ALERT] BAD FIELDS:',list(set(bad_fields))[:5]) if bad_fields else print('[OK] Fields OK')
print('[ALERT] BAD VALUES:',bad_vals[:3]) if bad_vals else print('[OK] Values OK')
except Exception as e:
print('[FAIL]',str(e))
print()
" {} \;- Commit
d52c01688f5697e60a1d3c598a4c30989403fb0f(commit message: "Update Ethereum RPCs") looks fine, but as always use your own node!
First, we verify that the claimed commit hash bafybeie5hxovqc4ifcnrnhvmjbefxgeix6oqvzaspyytdxiyscji22v5pu is correctly calculated. I use https://codeberg.org/torndao/tornado-ipfs-ui - the original version seemed fine to me until now & the latest commit is just the commit hash update: https://codeberg.org/torndao/tornado-ipfs-ui/commit/982f9f4efbe65e756dd5e3a47069613457558bc2.
~$ git clone https://codeberg.org/torndao/tornado-ipfs-ui.git
~$ docker build -t tornado-classic-ui .
~$ docker container run --rm -it --entrypoint cat tornado-classic-ui /app/ipfs_hash.txtwhich returns bafybeie5hxovqc4ifcnrnhvmjbefxgeix6oqvzaspyytdxiyscji22v5pu. So the claimed IPFS hash is correctly derived.
Let's use on-chain data to verify this as well. Let's get the ENS public resolver (0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e is the ENS registry):
cast call 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e "resolver(bytes32)(address)" $(cast namehash tornadocash.eth) -r https://eth.drpc.orgwhich returns 0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41.
Now we can get the IPFS hash (sorry some custom Bash magic lol):
cast call 0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41 "contenthash(bytes32)(bytes)" $(cast namehash tornadocash.eth) -r https://eth.drpc.org | sed 's/^0xe301//' | xxd -r -p | base32 | tr -d '=' | tr '[:upper:]' '[:lower:]' | sed 's/^a/ipfs:\/\/ba/'which returns ipfs://bafybeie5hxovqc4ifcnrnhvmjbefxgeix6oqvzaspyytdxiyscji22v5pu. Looks good!
Finally, let's cross-check with with the eth.limo headers (since we don't trust anyone):
curl -sI https://tornadocash.eth.limo/ | grep -i "x-ipfs"which returns
access-control-expose-headers: Content-Length,Content-Range,X-Chunked-Output,X-Ipfs-Path,X-Ipfs-Roots,X-Stream-Output
x-ipfs-path: /ipfs/bafybeie5hxovqc4ifcnrnhvmjbefxgeix6oqvzaspyytdxiyscji22v5pu/
x-ipfs-roots: bafybeie5hxovqc4ifcnrnhvmjbefxgeix6oqvzaspyytdxiyscji22v5puSo we're good here.
Thanks for the detailed verification on the yarn.lock integrity checks. Fair point on the RPC too, agree.