mirror of
https://github.com/TecharoHQ/anubis.git
synced 2026-04-12 19:48:44 +00:00
Compare commits
2 Commits
Xe/purge-s
...
Xe-patch-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8b792668e | ||
|
|
648b1b7eb2 |
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: 'bug:'
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Desktop (please complete the following information):**
|
||||||
|
- OS: [e.g. iOS]
|
||||||
|
- Browser [e.g. chrome, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Smartphone (please complete the following information):**
|
||||||
|
- Device: [e.g. iPhone6]
|
||||||
|
- OS: [e.g. iOS8.1]
|
||||||
|
- Browser [e.g. stock browser, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: 'feature:'
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
9
.github/ISSUE_TEMPLATE/security.md
vendored
Normal file
9
.github/ISSUE_TEMPLATE/security.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
name: Security report
|
||||||
|
about: Do not file security reports here. Email security@techaro.lol.
|
||||||
|
title: "security:"
|
||||||
|
labels: ""
|
||||||
|
assignees: Xe
|
||||||
|
---
|
||||||
|
|
||||||
|
Do not file security reports here. Email security@techaro.lol.
|
||||||
4
.github/actions/spelling/expect.txt
vendored
4
.github/actions/spelling/expect.txt
vendored
@@ -141,7 +141,6 @@ httpdebug
|
|||||||
Huawei
|
Huawei
|
||||||
hypertext
|
hypertext
|
||||||
iaskspider
|
iaskspider
|
||||||
iaso
|
|
||||||
iat
|
iat
|
||||||
ifm
|
ifm
|
||||||
Imagesift
|
Imagesift
|
||||||
@@ -233,7 +232,6 @@ promauto
|
|||||||
promhttp
|
promhttp
|
||||||
proofofwork
|
proofofwork
|
||||||
publicsuffix
|
publicsuffix
|
||||||
purejs
|
|
||||||
pwcmd
|
pwcmd
|
||||||
pwuser
|
pwuser
|
||||||
qualys
|
qualys
|
||||||
@@ -256,6 +254,7 @@ runtimedir
|
|||||||
runtimedirectory
|
runtimedirectory
|
||||||
sas
|
sas
|
||||||
sasl
|
sasl
|
||||||
|
screenshots
|
||||||
searchbot
|
searchbot
|
||||||
searx
|
searx
|
||||||
sebest
|
sebest
|
||||||
@@ -311,6 +310,7 @@ Varis
|
|||||||
Velen
|
Velen
|
||||||
vendored
|
vendored
|
||||||
vhosts
|
vhosts
|
||||||
|
videotest
|
||||||
VKE
|
VKE
|
||||||
Vultr
|
Vultr
|
||||||
waitloop
|
waitloop
|
||||||
|
|||||||
@@ -19,17 +19,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- The [`segments`](./admin/configuration/expressions.mdx#segments) function was added for splitting a path into its slash-separated segments.
|
- The [`segments`](./admin/configuration/expressions.mdx#segments) function was added for splitting a path into its slash-separated segments.
|
||||||
- When issuing a challenge, Anubis stores information about that challenge into the store. That stored information is later used to validate challenge responses. This works around nondeterminism in bot rules. ([#917](https://github.com/TecharoHQ/anubis/issues/917))
|
- When issuing a challenge, Anubis stores information about that challenge into the store. That stored information is later used to validate challenge responses. This works around nondeterminism in bot rules. ([#917](https://github.com/TecharoHQ/anubis/issues/917))
|
||||||
- When parsing [Open Graph tags](./admin/configuration/open-graph.mdx), add any URLs found in the responses to a temporary "allow cache" so that social preview images work.
|
- When parsing [Open Graph tags](./admin/configuration/open-graph.mdx), add any URLs found in the responses to a temporary "allow cache" so that social preview images work.
|
||||||
- Proof of work solving has had a complete overhaul and rethink based on feedback from browser engine developers, frontend experts, and overall performance profiling.
|
|
||||||
- One of the biggest sources of lag in Firefox has been eliminated: the use of WebCrypto. Now whenever Anubis detects the client is using Firefox (or Pale Moon), it will swap over to a pure-JS implementation of SHA-256 for speed.
|
|
||||||
- Web Workers are stored as dedicated JavaScript files in `static/js/workers/*.mjs`.
|
|
||||||
- Pave the way for non-SHA256 solver methods and eventually one that uses WebAssembly (or WebAssembly code compiled to JS for those that disable WebAssembly).
|
|
||||||
- Legacy JavaScript code has been eliminated.
|
|
||||||
- The contact email in the LibreJS header has been changed.
|
|
||||||
- The hard dependency on WebCrypto has been removed, allowing a proof of work challenge to work over plain (unencrypted) HTTP.
|
|
||||||
|
|
||||||
### Breaking changes
|
|
||||||
|
|
||||||
- The "slow" frontend solver has been removed in order to reduce maintenance burden. Any existing uses of it will still work, but issue a warning upon startup asking administrators to upgrade to the "fast" frontend solver.
|
|
||||||
|
|
||||||
## v1.21.3: Minfilia Warde - Echo 3
|
## v1.21.3: Minfilia Warde - Echo 3
|
||||||
|
|
||||||
|
|||||||
@@ -149,10 +149,6 @@ func ParseConfig(ctx context.Context, fin io.Reader, fname string, defaultDiffic
|
|||||||
if parsedBot.Challenge.Algorithm == "" {
|
if parsedBot.Challenge.Algorithm == "" {
|
||||||
parsedBot.Challenge.Algorithm = config.DefaultAlgorithm
|
parsedBot.Challenge.Algorithm = config.DefaultAlgorithm
|
||||||
}
|
}
|
||||||
|
|
||||||
if parsedBot.Challenge.Algorithm == "slow" {
|
|
||||||
slog.Warn("use of deprecated algorithm \"slow\" detected, please update this to \"fast\" when possible", "name", parsedBot.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.Weight != nil {
|
if b.Weight != nil {
|
||||||
@@ -167,10 +163,6 @@ func ParseConfig(ctx context.Context, fin io.Reader, fname string, defaultDiffic
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range c.Thresholds {
|
for _, t := range c.Thresholds {
|
||||||
if t.Challenge != nil && t.Challenge.Algorithm == "slow" {
|
|
||||||
slog.Warn("use of deprecated algorithm \"slow\" detected, please update this to \"fast\" when possible", "name", t.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Name == "legacy-anubis-behaviour" && t.Expression.String() == "true" {
|
if t.Name == "legacy-anubis-behaviour" && t.Expression.String() == "true" {
|
||||||
if !warnedAboutThresholds.Load() {
|
if !warnedAboutThresholds.Load() {
|
||||||
slog.Warn("configuration file does not contain thresholds, see docs for details on how to upgrade", "fname", fname, "docs_url", "https://anubis.techaro.lol/docs/admin/configuration/thresholds/")
|
slog.Warn("configuration file does not contain thresholds, see docs for details on how to upgrade", "fname", fname, "docs_url", "https://anubis.techaro.lol/docs/admin/configuration/thresholds/")
|
||||||
|
|||||||
97
package-lock.json
generated
97
package-lock.json
generated
@@ -8,9 +8,6 @@
|
|||||||
"name": "@techaro/anubis",
|
"name": "@techaro/anubis",
|
||||||
"version": "1.21.3",
|
"version": "1.21.3",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
|
||||||
"@aws-crypto/sha256-js": "^5.2.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cssnano": "^7.1.0",
|
"cssnano": "^7.1.0",
|
||||||
"cssnano-preset-advanced": "^7.0.8",
|
"cssnano-preset-advanced": "^7.0.8",
|
||||||
@@ -22,44 +19,6 @@
|
|||||||
"postcss-url": "^10.1.3"
|
"postcss-url": "^10.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-crypto/sha256-js": {
|
|
||||||
"version": "5.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz",
|
|
||||||
"integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@aws-crypto/util": "^5.2.0",
|
|
||||||
"@aws-sdk/types": "^3.222.0",
|
|
||||||
"tslib": "^2.6.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@aws-crypto/util": {
|
|
||||||
"version": "5.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz",
|
|
||||||
"integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@aws-sdk/types": "^3.222.0",
|
|
||||||
"@smithy/util-utf8": "^2.0.0",
|
|
||||||
"tslib": "^2.6.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@aws-sdk/types": {
|
|
||||||
"version": "3.840.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.840.0.tgz",
|
|
||||||
"integrity": "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@smithy/types": "^4.3.1",
|
|
||||||
"tslib": "^2.6.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.25.8",
|
"version": "0.25.8",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
|
||||||
@@ -502,56 +461,6 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@smithy/is-array-buffer": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.6.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@smithy/types": {
|
|
||||||
"version": "4.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz",
|
|
||||||
"integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.6.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@smithy/util-buffer-from": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@smithy/is-array-buffer": "^2.2.0",
|
|
||||||
"tslib": "^2.6.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@smithy/util-utf8": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz",
|
|
||||||
"integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@smithy/util-buffer-from": "^2.2.0",
|
|
||||||
"tslib": "^2.6.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ansi-regex": {
|
"node_modules/ansi-regex": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
@@ -2606,12 +2515,6 @@
|
|||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tslib": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
|
||||||
"license": "0BSD"
|
|
||||||
},
|
|
||||||
"node_modules/universalify": {
|
"node_modules/universalify": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
|||||||
@@ -26,8 +26,5 @@
|
|||||||
"postcss-import": "^16.1.1",
|
"postcss-import": "^16.1.1",
|
||||||
"postcss-import-url": "^7.2.0",
|
"postcss-import-url": "^7.2.0",
|
||||||
"postcss-url": "^10.1.3"
|
"postcss-url": "^10.1.3"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@aws-crypto/sha256-js": "^5.2.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
17
web/build.sh
17
web/build.sh
@@ -8,7 +8,7 @@ LICENSE='/*
|
|||||||
@licstart The following is the entire license notice for the
|
@licstart The following is the entire license notice for the
|
||||||
JavaScript code in this page.
|
JavaScript code in this page.
|
||||||
|
|
||||||
Copyright (c) 2025 Xe Iaso <xe.iaso@techaro.lol>
|
Copyright (c) 2025 Xe Iaso <me@xeiaso.net>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -28,9 +28,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
Includes code from https://github.com/aws/aws-sdk-js-crypto-helpers which is
|
|
||||||
used under the terms of the Apache 2 license.
|
|
||||||
|
|
||||||
@licend The above is the entire license notice
|
@licend The above is the entire license notice
|
||||||
for the JavaScript code in this page.
|
for the JavaScript code in this page.
|
||||||
*/'
|
*/'
|
||||||
@@ -39,9 +36,9 @@ for the JavaScript code in this page.
|
|||||||
mkdir -p static/locales
|
mkdir -p static/locales
|
||||||
cp ../lib/localization/locales/*.json static/locales/
|
cp ../lib/localization/locales/*.json static/locales/
|
||||||
|
|
||||||
for file in js/*.mjs js/worker/*.mjs; do
|
esbuild js/main.mjs --sourcemap --bundle --minify --outfile=static/js/main.mjs "--banner:js=${LICENSE}"
|
||||||
esbuild "${file}" --sourcemap --bundle --minify --outfile=static/"${file}" --banner:js="${LICENSE}"
|
gzip -f -k -n static/js/main.mjs
|
||||||
gzip -f -k -n static/${file}
|
zstd -f -k --ultra -22 static/js/main.mjs
|
||||||
zstd -f -k --ultra -22 static/${file}
|
brotli -fZk static/js/main.mjs
|
||||||
brotli -fZk static/${file}
|
|
||||||
done
|
esbuild js/bench.mjs --sourcemap --bundle --minify --outfile=static/js/bench.mjs
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
export default function process(
|
|
||||||
{ basePrefix, version },
|
|
||||||
data,
|
|
||||||
difficulty = 5,
|
|
||||||
signal = null,
|
|
||||||
progressCallback = null,
|
|
||||||
threads = Math.max(navigator.hardwareConcurrency / 2, 1),
|
|
||||||
) {
|
|
||||||
console.debug("fast algo");
|
|
||||||
|
|
||||||
let workerMethod = window.crypto !== undefined ? "webcrypto" : "purejs";
|
|
||||||
|
|
||||||
if (navigator.userAgent.includes("Firefox") || navigator.userAgent.includes("Goanna")) {
|
|
||||||
console.log("Firefox detected, using pure-JS fallback");
|
|
||||||
workerMethod = "purejs";
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let webWorkerURL = `${basePrefix}/.within.website/x/cmd/anubis/static/js/worker/sha256-${workerMethod}.mjs?cacheBuster=${version}`;
|
|
||||||
|
|
||||||
console.log(webWorkerURL);
|
|
||||||
|
|
||||||
const workers = [];
|
|
||||||
let settled = false;
|
|
||||||
|
|
||||||
const cleanup = () => {
|
|
||||||
if (settled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
settled = true;
|
|
||||||
workers.forEach((w) => w.terminate());
|
|
||||||
if (signal != null) {
|
|
||||||
signal.removeEventListener("abort", onAbort);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onAbort = () => {
|
|
||||||
console.log("PoW aborted");
|
|
||||||
cleanup();
|
|
||||||
reject(new DOMException("Aborted", "AbortError"));
|
|
||||||
};
|
|
||||||
|
|
||||||
if (signal != null) {
|
|
||||||
if (signal.aborted) {
|
|
||||||
return onAbort();
|
|
||||||
}
|
|
||||||
signal.addEventListener("abort", onAbort, { once: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < threads; i++) {
|
|
||||||
let worker = new Worker(webWorkerURL);
|
|
||||||
|
|
||||||
worker.onmessage = (event) => {
|
|
||||||
if (typeof event.data === "number") {
|
|
||||||
progressCallback?.(event.data);
|
|
||||||
} else {
|
|
||||||
cleanup();
|
|
||||||
resolve(event.data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
worker.onerror = (event) => {
|
|
||||||
cleanup();
|
|
||||||
reject(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
worker.postMessage({
|
|
||||||
data,
|
|
||||||
difficulty,
|
|
||||||
nonce: i,
|
|
||||||
threads,
|
|
||||||
});
|
|
||||||
|
|
||||||
workers.push(worker);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import fast from "./fast.mjs";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
fast: fast,
|
|
||||||
slow: fast, // XXX(Xe): slow is deprecated, but keep this around in case anything goes bad
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
import algorithms from "./algorithms/index.mjs";
|
import processFast from "./proof-of-work.mjs";
|
||||||
|
import processSlow from "./proof-of-work-slow.mjs";
|
||||||
|
|
||||||
const defaultDifficulty = 4;
|
const defaultDifficulty = 4;
|
||||||
|
const algorithms = {
|
||||||
|
fast: processFast,
|
||||||
|
slow: processSlow,
|
||||||
|
};
|
||||||
|
|
||||||
const status = document.getElementById("status");
|
const status = document.getElementById("status");
|
||||||
const difficultyInput = document.getElementById("difficulty-input");
|
const difficultyInput = document.getElementById("difficulty-input");
|
||||||
@@ -37,7 +42,7 @@ const benchmarkTrial = async (stats, difficulty, algorithm, signal) => {
|
|||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
const t0 = performance.now();
|
const t0 = performance.now();
|
||||||
const { hash, nonce } = await process({ basePrefix: "/", version: "devel" }, challenge, Number(difficulty), signal);
|
const { hash, nonce } = await process(challenge, Number(difficulty), signal);
|
||||||
const t1 = performance.now();
|
const t1 = performance.now();
|
||||||
console.log({ hash, nonce });
|
console.log({ hash, nonce });
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import algorithms from "./algorithms/index.mjs";
|
import processFast from "./proof-of-work.mjs";
|
||||||
|
import processSlow from "./proof-of-work-slow.mjs";
|
||||||
|
|
||||||
|
const algorithms = {
|
||||||
|
fast: processFast,
|
||||||
|
slow: processSlow,
|
||||||
|
};
|
||||||
|
|
||||||
// from Xeact
|
// from Xeact
|
||||||
const u = (url = "", params = {}) => {
|
const u = (url = "", params = {}) => {
|
||||||
@@ -69,6 +75,11 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
|
|||||||
await initTranslations();
|
await initTranslations();
|
||||||
|
|
||||||
const dependencies = [
|
const dependencies = [
|
||||||
|
{
|
||||||
|
name: "WebCrypto",
|
||||||
|
msg: t('web_crypto_error'),
|
||||||
|
value: window.crypto,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Web Workers",
|
name: "Web Workers",
|
||||||
msg: t('web_workers_error'),
|
msg: t('web_workers_error'),
|
||||||
@@ -108,6 +119,15 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
|
|||||||
progress.style.display = "none";
|
progress.style.display = "none";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!window.isSecureContext) {
|
||||||
|
ohNoes({
|
||||||
|
titleMsg: t('context_not_secure'),
|
||||||
|
statusMsg: t('context_not_secure_msg'),
|
||||||
|
imageSrc: imageURL("reject", anubisVersion, basePrefix),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
status.innerHTML = t('calculating');
|
status.innerHTML = t('calculating');
|
||||||
|
|
||||||
for (const { value, name, msg } of dependencies) {
|
for (const { value, name, msg } of dependencies) {
|
||||||
@@ -151,7 +171,6 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
|
|||||||
try {
|
try {
|
||||||
const t0 = Date.now();
|
const t0 = Date.now();
|
||||||
const { hash, nonce } = await process(
|
const { hash, nonce } = await process(
|
||||||
{ basePrefix, version: anubisVersion },
|
|
||||||
challenge,
|
challenge,
|
||||||
rules.difficulty,
|
rules.difficulty,
|
||||||
null,
|
null,
|
||||||
|
|||||||
99
web/js/proof-of-work-slow.mjs
Normal file
99
web/js/proof-of-work-slow.mjs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// https://dev.to/ratmd/simple-proof-of-work-in-javascript-3kgm
|
||||||
|
|
||||||
|
export default function process(
|
||||||
|
data,
|
||||||
|
difficulty = 5,
|
||||||
|
signal = null,
|
||||||
|
progressCallback = null,
|
||||||
|
_threads = 1,
|
||||||
|
) {
|
||||||
|
console.debug("slow algo");
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let webWorkerURL = URL.createObjectURL(
|
||||||
|
new Blob(["(", processTask(), ")()"], { type: "application/javascript" }),
|
||||||
|
);
|
||||||
|
|
||||||
|
let worker = new Worker(webWorkerURL);
|
||||||
|
let settled = false;
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
if (settled) return;
|
||||||
|
settled = true;
|
||||||
|
worker.terminate();
|
||||||
|
if (signal != null) {
|
||||||
|
signal.removeEventListener("abort", onAbort);
|
||||||
|
}
|
||||||
|
URL.revokeObjectURL(webWorkerURL);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAbort = () => {
|
||||||
|
console.log("PoW aborted");
|
||||||
|
cleanup();
|
||||||
|
reject(new DOMException("Aborted", "AbortError"));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (signal != null) {
|
||||||
|
if (signal.aborted) {
|
||||||
|
return onAbort();
|
||||||
|
}
|
||||||
|
signal.addEventListener("abort", onAbort, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.onmessage = (event) => {
|
||||||
|
if (typeof event.data === "number") {
|
||||||
|
progressCallback?.(event.data);
|
||||||
|
} else {
|
||||||
|
cleanup();
|
||||||
|
resolve(event.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.onerror = (event) => {
|
||||||
|
cleanup();
|
||||||
|
reject(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.postMessage({
|
||||||
|
data,
|
||||||
|
difficulty,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function processTask() {
|
||||||
|
return function () {
|
||||||
|
const sha256 = (text) => {
|
||||||
|
const encoded = new TextEncoder().encode(text);
|
||||||
|
return crypto.subtle.digest("SHA-256", encoded.buffer).then((result) =>
|
||||||
|
Array.from(new Uint8Array(result))
|
||||||
|
.map((c) => c.toString(16).padStart(2, "0"))
|
||||||
|
.join(""),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
addEventListener("message", async (event) => {
|
||||||
|
let data = event.data.data;
|
||||||
|
let difficulty = event.data.difficulty;
|
||||||
|
|
||||||
|
let hash;
|
||||||
|
let nonce = 0;
|
||||||
|
do {
|
||||||
|
if ((nonce & 1023) === 0) {
|
||||||
|
postMessage(nonce);
|
||||||
|
}
|
||||||
|
hash = await sha256(data + nonce++);
|
||||||
|
} while (
|
||||||
|
hash.substring(0, difficulty) !== Array(difficulty + 1).join("0")
|
||||||
|
);
|
||||||
|
|
||||||
|
nonce -= 1; // last nonce was post-incremented
|
||||||
|
|
||||||
|
postMessage({
|
||||||
|
hash,
|
||||||
|
data,
|
||||||
|
difficulty,
|
||||||
|
nonce,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}.toString();
|
||||||
|
}
|
||||||
137
web/js/proof-of-work.mjs
Normal file
137
web/js/proof-of-work.mjs
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
export default function process(
|
||||||
|
data,
|
||||||
|
difficulty = 5,
|
||||||
|
signal = null,
|
||||||
|
progressCallback = null,
|
||||||
|
threads = Math.max(navigator.hardwareConcurrency / 2, 1),
|
||||||
|
) {
|
||||||
|
console.debug("fast algo");
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let webWorkerURL = URL.createObjectURL(
|
||||||
|
new Blob(["(", processTask(), ")()"], { type: "application/javascript" }),
|
||||||
|
);
|
||||||
|
|
||||||
|
const workers = [];
|
||||||
|
let settled = false;
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
if (settled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
settled = true;
|
||||||
|
workers.forEach((w) => w.terminate());
|
||||||
|
if (signal != null) {
|
||||||
|
signal.removeEventListener("abort", onAbort);
|
||||||
|
}
|
||||||
|
URL.revokeObjectURL(webWorkerURL);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAbort = () => {
|
||||||
|
console.log("PoW aborted");
|
||||||
|
cleanup();
|
||||||
|
reject(new DOMException("Aborted", "AbortError"));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (signal != null) {
|
||||||
|
if (signal.aborted) {
|
||||||
|
return onAbort();
|
||||||
|
}
|
||||||
|
signal.addEventListener("abort", onAbort, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < threads; i++) {
|
||||||
|
let worker = new Worker(webWorkerURL);
|
||||||
|
|
||||||
|
worker.onmessage = (event) => {
|
||||||
|
if (typeof event.data === "number") {
|
||||||
|
progressCallback?.(event.data);
|
||||||
|
} else {
|
||||||
|
cleanup();
|
||||||
|
resolve(event.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.onerror = (event) => {
|
||||||
|
cleanup();
|
||||||
|
reject(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.postMessage({
|
||||||
|
data,
|
||||||
|
difficulty,
|
||||||
|
nonce: i,
|
||||||
|
threads,
|
||||||
|
});
|
||||||
|
|
||||||
|
workers.push(worker);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function processTask() {
|
||||||
|
return function () {
|
||||||
|
const sha256 = (text) => {
|
||||||
|
const encoded = new TextEncoder().encode(text);
|
||||||
|
return crypto.subtle.digest("SHA-256", encoded.buffer);
|
||||||
|
};
|
||||||
|
|
||||||
|
function uint8ArrayToHexString(arr) {
|
||||||
|
return Array.from(arr)
|
||||||
|
.map((c) => c.toString(16).padStart(2, "0"))
|
||||||
|
.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener("message", async (event) => {
|
||||||
|
let data = event.data.data;
|
||||||
|
let difficulty = event.data.difficulty;
|
||||||
|
let hash;
|
||||||
|
let nonce = event.data.nonce;
|
||||||
|
let threads = event.data.threads;
|
||||||
|
|
||||||
|
const threadId = nonce;
|
||||||
|
let localIterationCount = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const currentHash = await sha256(data + nonce);
|
||||||
|
const thisHash = new Uint8Array(currentHash);
|
||||||
|
let valid = true;
|
||||||
|
|
||||||
|
for (let j = 0; j < difficulty; j++) {
|
||||||
|
const byteIndex = Math.floor(j / 2); // which byte we are looking at
|
||||||
|
const nibbleIndex = j % 2; // which nibble in the byte we are looking at (0 is high, 1 is low)
|
||||||
|
|
||||||
|
let nibble =
|
||||||
|
(thisHash[byteIndex] >> (nibbleIndex === 0 ? 4 : 0)) & 0x0f; // Get the nibble
|
||||||
|
|
||||||
|
if (nibble !== 0) {
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid) {
|
||||||
|
hash = uint8ArrayToHexString(thisHash);
|
||||||
|
console.log(hash);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce += threads;
|
||||||
|
|
||||||
|
// send a progress update every 1024 iterations so that the user can be informed of
|
||||||
|
// the state of the challenge.
|
||||||
|
if (threadId == 0 && localIterationCount === 1024) {
|
||||||
|
postMessage(nonce);
|
||||||
|
localIterationCount = 0;
|
||||||
|
}
|
||||||
|
localIterationCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
postMessage({
|
||||||
|
hash,
|
||||||
|
data,
|
||||||
|
difficulty,
|
||||||
|
nonce,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}.toString();
|
||||||
|
}
|
||||||
16
web/js/video.mjs
Normal file
16
web/js/video.mjs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
const videoElement = `<video id="videotest" width="0" height="0" src="/.within.website/x/cmd/anubis/static/testdata/black.mp4"></video>`;
|
||||||
|
|
||||||
|
export const testVideo = async (testarea) => {
|
||||||
|
testarea.innerHTML = videoElement;
|
||||||
|
return await new Promise((resolve) => {
|
||||||
|
const video = document.getElementById("videotest");
|
||||||
|
video.oncanplay = () => {
|
||||||
|
testarea.style.display = "none";
|
||||||
|
resolve(true);
|
||||||
|
};
|
||||||
|
video.onerror = () => {
|
||||||
|
testarea.style.display = "none";
|
||||||
|
resolve(false);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import { Sha256 } from '@aws-crypto/sha256-js';
|
|
||||||
|
|
||||||
const calculateSHA256 = (text) => {
|
|
||||||
const hash = new Sha256();
|
|
||||||
hash.update(text);
|
|
||||||
return hash.digest();
|
|
||||||
};
|
|
||||||
|
|
||||||
function uint8ArrayToHexString(arr) {
|
|
||||||
return Array.from(arr)
|
|
||||||
.map((c) => c.toString(16).padStart(2, "0"))
|
|
||||||
.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
addEventListener('message', async ({ data: eventData }) => {
|
|
||||||
const { data, difficulty, threads } = eventData;
|
|
||||||
let nonce = eventData.nonce;
|
|
||||||
const isMainThread = nonce === 0;
|
|
||||||
let iterations = 0;
|
|
||||||
|
|
||||||
const requiredZeroBytes = Math.floor(difficulty / 2);
|
|
||||||
const isDifficultyOdd = difficulty % 2 !== 0;
|
|
||||||
|
|
||||||
for (; ;) {
|
|
||||||
const hashBuffer = await calculateSHA256(data + nonce);
|
|
||||||
const hashArray = new Uint8Array(hashBuffer);
|
|
||||||
|
|
||||||
let isValid = true;
|
|
||||||
for (let i = 0; i < requiredZeroBytes; i++) {
|
|
||||||
if (hashArray[i] !== 0) {
|
|
||||||
isValid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValid && isDifficultyOdd) {
|
|
||||||
if ((hashArray[requiredZeroBytes] >> 4) !== 0) {
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValid) {
|
|
||||||
const finalHash = toHexString(hashArray);
|
|
||||||
postMessage({
|
|
||||||
hash: finalHash,
|
|
||||||
data,
|
|
||||||
difficulty,
|
|
||||||
nonce,
|
|
||||||
});
|
|
||||||
return; // Exit worker
|
|
||||||
}
|
|
||||||
|
|
||||||
nonce += threads;
|
|
||||||
iterations++;
|
|
||||||
|
|
||||||
// Send a progress update from the main thread every 1024 iterations.
|
|
||||||
if (isMainThread && (iterations & 1023) === 0) {
|
|
||||||
postMessage(nonce);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
const encoder = new TextEncoder();
|
|
||||||
const calculateSHA256 = async (input) => {
|
|
||||||
const data = encoder.encode(input);
|
|
||||||
return await crypto.subtle.digest("SHA-256", data);
|
|
||||||
};
|
|
||||||
|
|
||||||
const toHexString = (byteArray) => {
|
|
||||||
return byteArray.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
|
|
||||||
};
|
|
||||||
|
|
||||||
addEventListener("message", async ({ data: eventData }) => {
|
|
||||||
const { data, difficulty, threads } = eventData;
|
|
||||||
let nonce = eventData.nonce;
|
|
||||||
const isMainThread = nonce === 0;
|
|
||||||
let iterations = 0;
|
|
||||||
|
|
||||||
const requiredZeroBytes = Math.floor(difficulty / 2);
|
|
||||||
const isDifficultyOdd = difficulty % 2 !== 0;
|
|
||||||
|
|
||||||
for (; ;) {
|
|
||||||
const hashBuffer = await calculateSHA256(data + nonce);
|
|
||||||
const hashArray = new Uint8Array(hashBuffer);
|
|
||||||
|
|
||||||
let isValid = true;
|
|
||||||
for (let i = 0; i < requiredZeroBytes; i++) {
|
|
||||||
if (hashArray[i] !== 0) {
|
|
||||||
isValid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValid && isDifficultyOdd) {
|
|
||||||
if ((hashArray[requiredZeroBytes] >> 4) !== 0) {
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValid) {
|
|
||||||
const finalHash = toHexString(hashArray);
|
|
||||||
postMessage({
|
|
||||||
hash: finalHash,
|
|
||||||
data,
|
|
||||||
difficulty,
|
|
||||||
nonce,
|
|
||||||
});
|
|
||||||
return; // Exit worker
|
|
||||||
}
|
|
||||||
|
|
||||||
nonce += threads;
|
|
||||||
iterations++;
|
|
||||||
|
|
||||||
// Send a progress update from the main thread every 1024 iterations.
|
|
||||||
if (isMainThread && (iterations & 1023) === 0) {
|
|
||||||
postMessage(nonce);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user