updates for pow-bot-deterrent-rp -- fixing cross domain webworker issue

This commit is contained in:
Your Name 2025-03-15 18:48:47 -05:00
parent 86a1d903dc
commit 9f8fc11ed5
9 changed files with 229 additions and 201 deletions

View File

@ -16,6 +16,5 @@ WORKDIR /app
COPY --from=build /build/pow-bot-deterrent /app/pow-bot-deterrent COPY --from=build /build/pow-bot-deterrent /app/pow-bot-deterrent
COPY static /app/static COPY static /app/static
COPY PoW_Bot_Deterrent_API_Tokens /app/PoW_Bot_Deterrent_API_Tokens COPY PoW_Bot_Deterrent_API_Tokens /app/PoW_Bot_Deterrent_API_Tokens
RUN chmod +x /app/pow-bot-deterrent
ENTRYPOINT ["/app/pow-bot-deterrent"] ENTRYPOINT ["/app/pow-bot-deterrent"]

View File

@ -142,7 +142,7 @@ Return type: `text/plain` (error/status messages only)
Otherwise it returns 404, 400, or 500. Otherwise it returns 404, 400, or 500.
#### `GET /static/<filename>` #### `GET /pow-bot-deterrent-static/<filename>`
Return type: depends on file Return type: depends on file
@ -236,7 +236,7 @@ When `pow-bot-deterrent.js` runs, if it finds an element with `data-pow-bot-dete
> 💬 *INFO* the element with the `pow-bot-deterrent` data properties should probably be styled to have a very small font size. When I was designing the css for the bot deterrent element, I made everything scale based on the font size (by using `em`). But because the page I was testing it on had a small font by default, I accidentally made it huge when it is rendered on a default HTML page. So for now you will want to make the font size of the element which contains it fairly small, like `10px` or `11px`. > 💬 *INFO* the element with the `pow-bot-deterrent` data properties should probably be styled to have a very small font size. When I was designing the css for the bot deterrent element, I made everything scale based on the font size (by using `em`). But because the page I was testing it on had a small font by default, I accidentally made it huge when it is rendered on a default HTML page. So for now you will want to make the font size of the element which contains it fairly small, like `10px` or `11px`.
#### `window.botBotDeterrentInit` #### `window.powBotDeterrentInit`
The bot deterrent event listeners, elements, css, & webworkers **won't be loaded until this function is called**. The bot deterrent event listeners, elements, css, & webworkers **won't be loaded until this function is called**.
@ -248,15 +248,15 @@ For example:
``` ```
<script> <script>
window.botBotDeterrentInit(); window.powBotDeterrentInit();
</script> </script>
``` ```
#### `window.powBotDeterrentReset` #### `window.powBotDeterrentReset`
Resets the bot deterrent(s), stops the webworkers, etc. Use this if you have updated the page and you need to call `window.botBotDeterrentInit` again. Resets the bot deterrent(s), stops the webworkers, etc. Use this if you have updated the page and you need to call `window.powBotDeterrentInit` again.
#### `window.botBotDeterrentInitDone` #### `window.powBotDeterrentInitDone`
A boolean variable that `pow-bot-deterrent.js` uses internally, so it can know if it has already been initialized or not. A boolean variable that `pow-bot-deterrent.js` uses internally, so it can know if it has already been initialized or not.
@ -296,10 +296,10 @@ function MyComponent({botDeterrentURL, challenge}) {
// Maybe less clear than the above, but JavaScript heads might enjoy this more: // Maybe less clear than the above, but JavaScript heads might enjoy this more:
// window[uniqueCallback] = setNonce; // window[uniqueCallback] = setNonce;
if(window.botBotDeterrentInitDone) { if(window.powBotDeterrentInitDone) {
window.powBotDeterrentReset(); window.powBotDeterrentReset();
} }
window.botBotDeterrentInit(); window.powBotDeterrentInit();
}, [uniqueCallback]); }, [uniqueCallback]);
return ( return (
@ -484,7 +484,7 @@ There are two main important parts, the form and the javascript at the bottom:
document.querySelector("form input[type='submit']").disabled = false; document.querySelector("form input[type='submit']").disabled = false;
}; };
</script> </script>
<script src="{{ .PowAPIURL }}/static/pow-bot-deterrent.js"></script> <script src="{{ .PowAPIURL }}/pow-bot-deterrent-static/pow-bot-deterrent.js"></script>
``` ```
⚠️ **NOTE** that the element with the `pow-bot-deterrent` data properties is placed **inside a form element**. This is required because the bot deterrent needs to know which input elements it should trigger on. We only want it to trigger when the user actually intends to submit the form; otherwise we are wasting a lot of their CPU cycles for no reason! ⚠️ **NOTE** that the element with the `pow-bot-deterrent` data properties is placed **inside a form element**. This is required because the bot deterrent needs to know which input elements it should trigger on. We only want it to trigger when the user actually intends to submit the form; otherwise we are wasting a lot of their CPU cycles for no reason!

View File

@ -1,6 +1,10 @@
#!/bin/bash -e #!/bin/bash -e
VERSION="0.0.13" tag="0.0.0"
if git describe --tags --abbrev=0 > /dev/null 2>&1 ; then
tag="$(git describe --tags --abbrev=0)"
fi
VERSION="$tag-$(git rev-parse --short HEAD)-$(hexdump -n 2 -ve '1/1 "%.2x"' /dev/urandom)"
rm -rf dockerbuild || true rm -rf dockerbuild || true
mkdir dockerbuild mkdir dockerbuild
@ -9,17 +13,17 @@ cp Dockerfile dockerbuild/Dockerfile-amd64
cp Dockerfile dockerbuild/Dockerfile-arm cp Dockerfile dockerbuild/Dockerfile-arm
cp Dockerfile dockerbuild/Dockerfile-arm64 cp Dockerfile dockerbuild/Dockerfile-arm64
sed -E 's|FROM alpine|FROM amd64/alpine|' -i dockerbuild/Dockerfile-amd64 sed -E 's|FROM alpine|FROM --platform=linux/amd64 alpine|' -i dockerbuild/Dockerfile-amd64
sed -E 's|FROM alpine|FROM arm32v7/alpine|' -i dockerbuild/Dockerfile-arm sed -E 's|FROM alpine|FROM --platform=linux/arm/v7 alpine|' -i dockerbuild/Dockerfile-arm
sed -E 's|FROM alpine|FROM arm64v8/alpine|' -i dockerbuild/Dockerfile-arm64 sed -E 's|FROM alpine|FROM --platform=linux/arm64/v8 alpine|' -i dockerbuild/Dockerfile-arm64
sed -E 's/GOARCH=/GOARCH=amd64/' -i dockerbuild/Dockerfile-amd64 sed -E 's/GOARCH=/GOARCH=amd64/' -i dockerbuild/Dockerfile-amd64
sed -E 's/GOARCH=/GOARCH=arm/' -i dockerbuild/Dockerfile-arm sed -E 's/GOARCH=/GOARCH=arm/' -i dockerbuild/Dockerfile-arm
sed -E 's/GOARCH=/GOARCH=arm64/' -i dockerbuild/Dockerfile-arm64 sed -E 's/GOARCH=/GOARCH=arm64/' -i dockerbuild/Dockerfile-arm64
docker build -f dockerbuild/Dockerfile-amd64 -t sequentialread/pow-bot-deterrent:$VERSION-amd64 . docker build --progress=plain -f dockerbuild/Dockerfile-amd64 -t sequentialread/pow-bot-deterrent:$VERSION-amd64 .
docker build -f dockerbuild/Dockerfile-arm -t sequentialread/pow-bot-deterrent:$VERSION-arm . docker build --progress=plain -f dockerbuild/Dockerfile-arm -t sequentialread/pow-bot-deterrent:$VERSION-arm .
docker build -f dockerbuild/Dockerfile-arm64 -t sequentialread/pow-bot-deterrent:$VERSION-arm64 . docker build --progress=plain -f dockerbuild/Dockerfile-arm64 -t sequentialread/pow-bot-deterrent:$VERSION-arm64 .
docker push sequentialread/pow-bot-deterrent:$VERSION-amd64 docker push sequentialread/pow-bot-deterrent:$VERSION-amd64
docker push sequentialread/pow-bot-deterrent:$VERSION-arm docker push sequentialread/pow-bot-deterrent:$VERSION-arm

View File

@ -47,31 +47,6 @@
document.querySelector("form input[type='submit']").disabled = false; document.querySelector("form input[type='submit']").disabled = false;
}; };
</script> </script>
<script src="/static/pow-bot-deterrent.js"></script> <script src="/pow-bot-deterrent-static/pow-bot-deterrent.js"></script>
<!-- <script src='./static/scrypt_wasm.js'></script>
<script>
const { scrypt } = wasm_bindgen;
async function run() {
console.log("a");
await wasm_bindgen();
console.log(scrypt(hexEncode('password in hex'), hexEncode('password in hex'), 4096, 8, 1, 16))
}
run();
function hexEncode(s){
var hex, i;
var result = "";
for (i=0; i<s.length; i++) {
hex = s.charCodeAt(i).toString(16);
result += ("000"+hex).slice(-4);
}
return result
}
</script> -->
</body> </body>
</html> </html>

170
main.go
View File

@ -14,15 +14,34 @@ import (
"os/exec" "os/exec"
"path" "path"
"path/filepath" "path/filepath"
"reflect"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"time" "time"
configlite "git.sequentialread.com/forest/config-lite"
errors "git.sequentialread.com/forest/pkg-errors" errors "git.sequentialread.com/forest/pkg-errors"
"golang.org/x/crypto/scrypt" "golang.org/x/crypto/scrypt"
) )
type Config struct {
ListenPort int `json:"listen_port"`
BatchSize int `json:"batch_size"`
DeprecateAfterBatches int `json:"deprecate_after_batches"`
ScryptCPUAndMemoryCost int `json:"scrypt_cpu_and_memory_cost"`
AdminAPIToken string `json:"admin_api_token"`
EmailAddress string `json:"email_address"`
// port 993 (IMAPS)
// port 143 (STARTTLS) [deprecated!]
ImapHost string `json:"imap_host"`
ImapPort int `json:"imap_port"`
ImapEncryption string `json:"imap_encryption"`
ImapUsername string `json:"imap_username"`
ImapPassword string `json:"imap_password"`
}
// https://en.wikipedia.org/wiki/Scrypt // https://en.wikipedia.org/wiki/Scrypt
type ScryptParameters struct { type ScryptParameters struct {
CPUAndMemoryCost int `json:"N"` CPUAndMemoryCost int `json:"N"`
@ -38,6 +57,9 @@ type Challenge struct {
DifficultyLevel int `json:"dl"` DifficultyLevel int `json:"dl"`
} }
var config Config
var appDirectory string
var scryptParameters ScryptParameters
var currentChallengesGeneration = map[string]int{} var currentChallengesGeneration = map[string]int{}
var challenges = map[string]map[string]int{} var challenges = map[string]map[string]int{}
@ -45,51 +67,7 @@ func main() {
var err error var err error
batchSize := 1000 apiTokensFolder := readConfiguration()
deprecateAfterBatches := 10
portNumber := 2370
scryptCPUAndMemoryCost := 16384
batchSizeEnv := os.ExpandEnv("$POW_BOT_DETERRENT_BATCH_SIZE")
deprecateAfterBatchesEnv := os.ExpandEnv("$POW_BOT_DETERRENT_DEPRECATE_AFTER_BATCHES")
portNumberEnv := os.ExpandEnv("$POW_BOT_DETERRENT_LISTEN_PORT")
scryptCPUAndMemoryCostEnv := os.ExpandEnv("$POW_BOT_DETERRENT_SCRYPT_CPU_AND_MEMORY_COST")
if batchSizeEnv != "" {
batchSize, err = strconv.Atoi(batchSizeEnv)
if err != nil {
panic(errors.Wrapf(err, "can't start the app because the POW_BOT_DETERRENT_BATCH_SIZE '%s' can't be converted to an integer", batchSizeEnv))
}
}
if deprecateAfterBatchesEnv != "" {
deprecateAfterBatches, err = strconv.Atoi(deprecateAfterBatchesEnv)
if err != nil {
panic(errors.Wrapf(err, "can't start the app because the POW_BOT_DETERRENT_DEPRECATE_AFTER_BATCHES '%s' can't be converted to an integer", deprecateAfterBatchesEnv))
}
}
if portNumberEnv != "" {
portNumber, err = strconv.Atoi(portNumberEnv)
if err != nil {
panic(errors.Wrapf(err, "can't start the app because the POW_BOT_DETERRENT_LISTEN_PORT '%s' can't be converted to an integer", portNumberEnv))
}
}
if scryptCPUAndMemoryCostEnv != "" {
scryptCPUAndMemoryCost, err = strconv.Atoi(scryptCPUAndMemoryCostEnv)
if err != nil {
panic(errors.Wrapf(err, "can't start the app because the POW_BOT_DETERRENT_SCRYPT_CPU_AND_MEMORY_COST '%s' can't be converted to an integer", scryptCPUAndMemoryCostEnv))
}
}
apiTokensFolder := locateAPITokensFolder()
adminAPIToken := os.ExpandEnv("$POW_BOT_DETERRENT_ADMIN_API_TOKEN")
if adminAPIToken == "" {
panic(errors.New("can't start the app, the POW_BOT_DETERRENT_ADMIN_API_TOKEN environment variable is required"))
}
scryptParameters := ScryptParameters{
CPUAndMemoryCost: scryptCPUAndMemoryCost,
BlockSize: 8,
Paralellization: 1,
KeyLength: 16,
}
requireMethod := func(method string) func(http.ResponseWriter, *http.Request) bool { requireMethod := func(method string) func(http.ResponseWriter, *http.Request) bool {
return func(responseWriter http.ResponseWriter, request *http.Request) bool { return func(responseWriter http.ResponseWriter, request *http.Request) bool {
@ -103,7 +81,7 @@ func main() {
} }
requireAdmin := func(responseWriter http.ResponseWriter, request *http.Request) bool { requireAdmin := func(responseWriter http.ResponseWriter, request *http.Request) bool {
if request.Header.Get("Authorization") != fmt.Sprintf("Bearer %s", adminAPIToken) { if request.Header.Get("Authorization") != fmt.Sprintf("Bearer %s", config.AdminAPIToken) {
http.Error(responseWriter, "401 Unauthorized", http.StatusUnauthorized) http.Error(responseWriter, "401 Unauthorized", http.StatusUnauthorized)
return true return true
} }
@ -262,8 +240,8 @@ func main() {
return true return true
} }
toReturn := make([]string, batchSize) toReturn := make([]string, config.BatchSize)
for i := 0; i < batchSize; i++ { for i := 0; i < config.BatchSize; i++ {
preimageBytes := make([]byte, 8) preimageBytes := make([]byte, 8)
_, err := rand.Read(preimageBytes) _, err := rand.Read(preimageBytes)
if err != nil { if err != nil {
@ -309,7 +287,7 @@ func main() {
} }
toRemove := []string{} toRemove := []string{}
for k, generation := range challenges[token] { for k, generation := range challenges[token] {
if generation+deprecateAfterBatches < currentChallengesGeneration[token] { if generation+config.DeprecateAfterBatches < currentChallengesGeneration[token] {
toRemove = append(toRemove, k) toRemove = append(toRemove, k)
} }
} }
@ -417,11 +395,23 @@ func main() {
return true return true
}) })
http.HandleFunc("/static/captcha.css", func(responseWriter http.ResponseWriter, request *http.Request) {
bytez, _ := os.ReadFile("./static/pow-bot-deterrent.css")
responseWriter.Header().Set("Content-Type", "text/css")
responseWriter.Write(bytez)
})
http.HandleFunc("/static/captcha.js", func(responseWriter http.ResponseWriter, request *http.Request) {
bytez, _ := os.ReadFile("./static/pow-bot-deterrent.js")
responseWriter.Header().Set("Content-Type", "application/javascript")
responseWriter.Write(bytez)
})
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static/")))) http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static/"))))
http.Handle("/pow-bot-deterrent-static/", http.StripPrefix("/pow-bot-deterrent-static/", http.FileServer(http.Dir("./static/"))))
log.Printf("💥 PoW! Bot Deterrent server listening on port %d", portNumber) log.Printf("💥 PoW! Bot Deterrent server listening on port %d", config.ListenPort)
err = http.ListenAndServe(fmt.Sprintf(":%d", portNumber), nil) err = http.ListenAndServe(fmt.Sprintf(":%d", config.ListenPort), nil)
// if got this far it means server crashed! // if got this far it means server crashed!
panic(err) panic(err)
@ -489,3 +479,81 @@ func getCurrentExecDir() (dir string, err error) {
return dir, nil return dir, nil
} }
func readConfiguration() string {
apiTokensFolderPath := locateAPITokensFolder()
appDirectory = filepath.Dir(apiTokensFolderPath)
configJsonPath := filepath.Join(appDirectory, "config.json")
err := configlite.ReadConfiguration(configJsonPath, "POW_BOT_DETERRENT", []string{}, reflect.ValueOf(&config))
if err != nil {
panic(errors.Wrap(err, "ReadConfiguration returned"))
}
errors := []string{}
if config.ListenPort == 0 {
config.ListenPort = 2370
}
if config.BatchSize == 0 {
config.BatchSize = 1000
}
if config.DeprecateAfterBatches == 0 {
config.DeprecateAfterBatches = 10
}
if config.ScryptCPUAndMemoryCost == 0 {
config.ScryptCPUAndMemoryCost = 16384
}
if config.AdminAPIToken == "" {
errors = append(errors, "the POW_BOT_DETERRENT_ADMIN_API_TOKEN environment variable is required")
}
if config.EmailAddress != "" {
if config.ImapHost == "" {
config.ImapHost = "localhost"
}
if config.ImapPort == 0 {
config.ImapPort = 993
}
if config.ImapEncryption == "" {
config.ImapEncryption = "SMTPS"
}
if config.ImapEncryption != "STARTTLS" && config.ImapEncryption != "IMAPS" && config.ImapEncryption != "NONE" {
errors = append(errors, fmt.Sprintf("ImapEncryption '%s' must be IMAPS, STARTTLS or NONE", config.ImapEncryption))
}
if config.ImapUsername == "" {
errors = append(errors, "ImapUsername is required")
}
if config.ImapPassword == "" {
errors = append(errors, "ImapPassword is required")
}
}
if len(errors) > 0 {
log.Fatalln("💥 PoW Bot Deterrent can't start because there are configuration issues:")
log.Fatalln(strings.Join(errors, "\n"))
}
scryptParameters = ScryptParameters{
CPUAndMemoryCost: config.ScryptCPUAndMemoryCost,
BlockSize: 8,
Paralellization: 1,
KeyLength: 16,
}
log.Println("💥 PoW Bot Deterrent starting up with config:")
configToLogBytes, _ := json.MarshalIndent(config, "", " ")
configToLogString := regexp.MustCompile(
`("admin_api_token": ")[^"]+(",)`,
).ReplaceAllString(
string(configToLogBytes),
"$1******$2",
)
configToLogString = regexp.MustCompile(
`("imap_password": ")[^"]+(",?)`,
).ReplaceAllString(
configToLogString,
"$1******$2",
)
log.Println(configToLogString)
return apiTokensFolderPath
}

View File

@ -7,6 +7,7 @@
let scrypt; let scrypt;
let scryptPromise; let scryptPromise;
let wasm;
let working = false; let working = false;
const batchSize = 8; const batchSize = 8;
@ -125,7 +126,7 @@ onmessage = function(e) {
} }
}; };
if(scrypt) { if(wasm && scrypt) {
doWork(); doWork();
} else { } else {
scryptPromise.then(() => { scryptPromise.then(() => {

View File

@ -3,31 +3,27 @@
const numberOfWebWorkersToCreate = 4; const numberOfWebWorkersToCreate = 4;
window.powBotDeterrentReset = () => { window.powBotDeterrentReset = () => {
window.botBotDeterrentInitDone = false; window.powBotDeterrentInitDone = false;
}; };
window.botBotDeterrentInit = () => { const trimSlashes = x => x.replace(/^\/|\/$/g, '');
if(window.botBotDeterrentInitDone) {
console.error("botBotDeterrentInit was called twice!"); window.powBotDeterrentInit = () => {
if(window.powBotDeterrentInitDone) {
console.error("powBotDeterrentInit was called twice!");
return return
} }
window.botBotDeterrentInitDone = true; window.powBotDeterrentInitDone = true;
const challenges = Array.from(document.querySelectorAll("[data-pow-bot-deterrent-challenge]")); const challenges = Array.from(document.querySelectorAll("[data-pow-bot-deterrent-challenge]"));
const challengesMap = {}; const challengesMap = {};
let url = null; let staticAssetsPath = trimSlashes("/pow-bot-deterrent-static/")
let proofOfWorker = { postMessage: () => console.error("error: proofOfWorker was never loaded. ") }; let proofOfWorker = { postMessage: () => console.error("error: proofOfWorker was never loaded. ") };
challenges.forEach(element => { challenges.forEach(element => {
if(!url) { if(element.dataset.powBotDeterrentStaticAssetsPath) {
if(!element.dataset.powBotDeterrentUrl) { staticAssetsPath = trimSlashes(element.dataset.powBotDeterrentStaticAssetsPath);
console.error("error: element with data-pow-bot-deterrent-challenge property is missing the data-pow-bot-deterrent-url property");
}
url = element.dataset.powBotDeterrentUrl;
if(url.endsWith("/")) {
url = url.substring(0, url.length-1)
}
} }
if(!element.dataset.powBotDeterrentCallback) { if(!element.dataset.powBotDeterrentCallback) {
@ -62,7 +58,7 @@
//todo //todo
} }
let cssIsAlreadyLoaded = document.querySelector(`link[href='${url}/static/pow-bot-deterrent.css']`); let cssIsAlreadyLoaded = document.querySelector(`link[href='/${staticAssetsPath}/pow-bot-deterrent.css']`);
cssIsAlreadyLoaded = cssIsAlreadyLoaded || Array.from(document.styleSheets).some(x => { cssIsAlreadyLoaded = cssIsAlreadyLoaded || Array.from(document.styleSheets).some(x => {
try { try {
@ -78,7 +74,7 @@
"charset": "utf8", "charset": "utf8",
}); });
stylesheet.onload = () => renderProgressInfo(element); stylesheet.onload = () => renderProgressInfo(element);
stylesheet.setAttribute("href", `${url}/static/pow-bot-deterrent.css`); stylesheet.setAttribute("href", `${staticAssetsPath}/pow-bot-deterrent.css`);
} else { } else {
renderProgressInfo(element); renderProgressInfo(element);
} }
@ -133,104 +129,88 @@
//todo //todo
} }
if(url) { let webWorkers;
webWorkers = [...Array(numberOfWebWorkersToCreate)].map((_, i) => {
// // https://stackoverflow.com/questions/21913673/execute-web-worker-from-different-origin/62914052#62914052 const webWorker = new Worker(`/${staticAssetsPath}/proofOfWorker.js`);
// const webWorkerUrlWhichIsProbablyCrossOrigin = `${url}/static/proofOfWorker.js`; webWorker.onmessage = function(e) {
const challengeState = challengesMap[e.data.challenge]
// const webWorkerPointerDataURL = URL.createObjectURL( if(!challengeState) {
// new Blob( console.error(`error: webworker sent message with unknown challenge '${e.data.challenge}'`);
// [ `importScripts( "${ webWorkerUrlWhichIsProbablyCrossOrigin }" );` ], }
// { type: "text/javascript" } if(e.data.type == "progress") {
// ) challengeState.difficulty = e.data.difficulty;
// ); challengeState.probabilityOfFailurePerAttempt = e.data.probabilityOfFailurePerAttempt;
if(!challengeState.smallestHash || challengeState.smallestHash > e.data.smallestHash) {
// return challengeState.smallestHash = e.data.smallestHash;
let webWorkers;
webWorkers = [...Array(numberOfWebWorkersToCreate)].map((_, i) => {
const webWorker = new Worker('/static/proofOfWorker.js');
webWorker.onmessage = function(e) {
const challengeState = challengesMap[e.data.challenge]
if(!challengeState) {
console.error(`error: webworker sent message with unknown challenge '${e.data.challenge}'`);
} }
if(e.data.type == "progress") { challengeState.attempts += e.data.attempts;
challengeState.difficulty = e.data.difficulty; } else if(e.data.type == "success") {
challengeState.probabilityOfFailurePerAttempt = e.data.probabilityOfFailurePerAttempt; if(!challengeState.done) {
if(!challengeState.smallestHash || challengeState.smallestHash > e.data.smallestHash) { challengeState.done = true;
challengeState.smallestHash = e.data.smallestHash; clearInterval(challengeState.updateProgressInterval);
}
challengeState.attempts += e.data.attempts; const element = challengeState.element;
} else if(e.data.type == "success") { const progressBar = element.querySelector(".pow-bot-deterrent-progress-bar");
if(!challengeState.done) { const checkmark = element.querySelector(".pow-checkmark-icon");
challengeState.done = true; const gears = element.querySelector(".pow-gears-icon");
clearInterval(challengeState.updateProgressInterval); const bestHashElement = element.querySelector(".pow-bot-deterrent-best-hash");
const description = element.querySelector(".pow-bot-deterrent-description");
const element = challengeState.element; challengeState.smallestHash = e.data.smallestHash;
const progressBar = element.querySelector(".pow-bot-deterrent-progress-bar"); bestHashElement.textContent = getHashProgressText(challengeState);
const checkmark = element.querySelector(".pow-checkmark-icon"); bestHashElement.classList.add("pow-bot-deterrent-best-hash-done");
const gears = element.querySelector(".pow-gears-icon"); checkmark.style.display = "block";
const bestHashElement = element.querySelector(".pow-bot-deterrent-best-hash"); checkmark.style.animationPlayState = "running";
const description = element.querySelector(".pow-bot-deterrent-description"); gears.style.display = "none";
challengeState.smallestHash = e.data.smallestHash; progressBar.style.width = "100%";
bestHashElement.textContent = getHashProgressText(challengeState);
bestHashElement.classList.add("pow-bot-deterrent-best-hash-done"); description.innerHTML = "";
checkmark.style.display = "block"; createElement(
checkmark.style.animationPlayState = "running"; description,
gears.style.display = "none"; "a",
progressBar.style.width = "100%"; {"href": "https://en.wikipedia.org/wiki/Proof_of_work"},
"PoW"
description.innerHTML = ""; );
createElement( appendFragment(description, " complete, you may continue.");
description, createElement(description, "br");
"a", appendFragment(description, "Privacy-respecting anti-spam measure.");
{"href": "https://en.wikipedia.org/wiki/Proof_of_work"},
"PoW" webWorkers.forEach(x => x.postMessage({stop: "STOP"}));
);
appendFragment(description, " complete, you may continue."); const callback = getCallbackFromGlobalNamespace(element.dataset.powBotDeterrentCallback);
createElement(description, "br"); if(!callback) {
appendFragment(description, "Privacy-respecting anti-spam measure."); console.error(`error: data-pow-bot-deterrent-callback '${element.dataset.powBotDeterrentCallback}' `
+ "is not defined in the global namespace!");
webWorkers.forEach(x => x.postMessage({stop: "STOP"}));
const callback = getCallbackFromGlobalNamespace(element.dataset.powBotDeterrentCallback);
if(!callback) {
console.error(`error: data-pow-bot-deterrent-callback '${element.dataset.powBotDeterrentCallback}' `
+ "is not defined in the global namespace!");
} else {
console.log(`firing callback for challenge ${e.data.challenge} w/ nonce ${e.data.nonce}, smallestHash: ${e.data.smallestHash}, difficulty: ${e.data.difficulty}`);
callback(e.data.nonce);
}
} else { } else {
console.log("success recieved twice"); console.log(`firing callback for challenge ${e.data.challenge} w/ nonce ${e.data.nonce}, smallestHash: ${e.data.smallestHash}, difficulty: ${e.data.difficulty}`);
callback(e.data.nonce);
} }
} else if(e.data.type == "error") {
console.error(`error: webworker errored out: '${e.data.message}'`);
} else { } else {
console.error(`error: webworker sent message with unknown type '${e.data.type}'`); console.log("success recieved twice");
} }
}; } else if(e.data.type == "error") {
return webWorker; console.error(`error: webworker errored out: '${e.data.message}'`);
}); } else {
console.error(`error: webworker sent message with unknown type '${e.data.type}'`);
// URL.revokeObjectURL(webWorkerPointerDataURL); }
proofOfWorker = {
postMessage: arg => webWorkers.forEach((x, i) => {
x.postMessage({ ...arg, workerId: i })
})
}; };
return webWorker;
});
window.powBotDeterrentReset = () => { proofOfWorker = {
window.botBotDeterrentInitDone = false; postMessage: arg => webWorkers.forEach((x, i) => {
webWorkers.forEach(x => x.terminate()); x.postMessage({ ...arg, workerId: i })
}; })
} };
window.powBotDeterrentReset = () => {
window.powBotDeterrentInitDone = false;
webWorkers.forEach(x => x.terminate());
};
}; };
const challenges = Array.from(document.querySelectorAll("[data-pow-bot-deterrent-challenge]")); const challenges = Array.from(document.querySelectorAll("[data-pow-bot-deterrent-challenge]"));
if(challenges.length) { if(challenges.length) {
window.botBotDeterrentInit(); window.powBotDeterrentInit();
} }
function getCallbackFromGlobalNamespace(callbackString) { function getCallbackFromGlobalNamespace(callbackString) {

View File

@ -7,6 +7,7 @@
let scrypt; let scrypt;
let scryptPromise; let scryptPromise;
let wasm = undefined;
let working = false; let working = false;
const batchSize = 4; const batchSize = 4;
@ -149,7 +150,6 @@ let wasm_bindgen;
if (typeof document !== 'undefined' && document.currentScript !== null) { if (typeof document !== 'undefined' && document.currentScript !== null) {
script_src = new URL(document.currentScript.src, location.href).toString(); script_src = new URL(document.currentScript.src, location.href).toString();
} }
let wasm = undefined;
let WASM_VECTOR_LEN = 0; let WASM_VECTOR_LEN = 0;
@ -365,11 +365,11 @@ let wasm_bindgen;
return __wbg_finalize_init(instance, module); return __wbg_finalize_init(instance, module);
} }
wasm_bindgen = Object.assign(__wbg_init, { initSync }, __exports); /pow-bot-deterrent-static/_bindgen = Object.assign(__wbg_init, { initSync }, __exports);
})(); })();
scrypt = wasm_bindgen.scrypt; scrypt = wasm_bindgen.scrypt;
scryptPromise = wasm_bindgen({module_or_path: "/static/scrypt.wasm"}); scryptPromise = wasm_bindgen({module_or_path: "/pow-bot-deterrent-static/scrypt.wasm"});

View File

@ -46,7 +46,8 @@ echo '
cat ../proofOfWorkerStub.js | tail -n +6 >> ../static/proofOfWorker.js cat ../proofOfWorkerStub.js | tail -n +6 >> ../static/proofOfWorker.js
cat scrypt-wasm/pkg/scrypt_wasm.js >> ../static/proofOfWorker.js # wasm was defined at the top of proofOfWorker.js, so don't define it again.
cat scrypt-wasm/pkg/scrypt_wasm.js | grep -v 'let wasm = ' >> ../static/proofOfWorker.js
# see: https://rustwasm.github.io/docs/wasm-bindgen/examples/without-a-bundler.html # see: https://rustwasm.github.io/docs/wasm-bindgen/examples/without-a-bundler.html
echo ' echo '