Closer file structure to desired
This commit is contained in:
103
embed/challenge/js-pow-sha256/runtime/runtime.go
Normal file
103
embed/challenge/js-pow-sha256/runtime/runtime.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"git.gammaspectra.live/git/go-away/lib/challenge"
|
||||
"git.gammaspectra.live/git/go-away/lib/challenge/inline"
|
||||
"math/bits"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//go:generate tinygo build -target wasip1 -buildmode=c-shared -opt=2 -scheduler=none -gc=leaking -no-debug -o runtime.wasm runtime.go
|
||||
func main() {
|
||||
|
||||
}
|
||||
|
||||
func getChallenge(key []byte, params map[string]string) ([]byte, uint64) {
|
||||
difficulty := uint64(20)
|
||||
var err error
|
||||
if diffStr, ok := params["difficulty"]; ok {
|
||||
difficulty, err = strconv.ParseUint(diffStr, 10, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
hasher := sha256.New()
|
||||
hasher.Write(binary.LittleEndian.AppendUint64(nil, difficulty))
|
||||
hasher.Write(key)
|
||||
return hasher.Sum(nil), difficulty
|
||||
}
|
||||
|
||||
//go:wasmexport MakeChallenge
|
||||
func MakeChallenge(in challenge.Allocation) (out challenge.Allocation) {
|
||||
return challenge.MakeChallengeDecode(func(in challenge.MakeChallengeInput, out *challenge.MakeChallengeOutput) {
|
||||
c, difficulty := getChallenge(in.Key, in.Parameters)
|
||||
|
||||
// create target
|
||||
target := make([]byte, len(c))
|
||||
nBits := difficulty
|
||||
for i := 0; i < len(target); i++ {
|
||||
var v uint8
|
||||
for j := 0; j < 8; j++ {
|
||||
v <<= 1
|
||||
if nBits == 0 {
|
||||
v |= 1
|
||||
} else {
|
||||
nBits--
|
||||
}
|
||||
}
|
||||
target[i] = v
|
||||
}
|
||||
|
||||
dst := make([]byte, inline.EncodedLen(len(c)))
|
||||
dst = dst[:inline.Encode(dst, c)]
|
||||
|
||||
targetDst := make([]byte, inline.EncodedLen(len(target)))
|
||||
targetDst = targetDst[:inline.Encode(targetDst, target)]
|
||||
|
||||
out.Data = []byte("{\"challenge\": \"" + string(dst) + "\", \"target\": \"" + string(targetDst) + "\", \"difficulty\": " + strconv.FormatUint(difficulty, 10) + "}")
|
||||
out.Headers.Set("Content-Type", "application/json; charset=utf-8")
|
||||
}, in)
|
||||
}
|
||||
|
||||
//go:wasmexport VerifyChallenge
|
||||
func VerifyChallenge(in challenge.Allocation) (out challenge.VerifyChallengeOutput) {
|
||||
return challenge.VerifyChallengeDecode(func(in challenge.VerifyChallengeInput) challenge.VerifyChallengeOutput {
|
||||
c, difficulty := getChallenge(in.Key, in.Parameters)
|
||||
|
||||
result := make([]byte, inline.DecodedLen(len(in.Result)))
|
||||
n, err := inline.Decode(result, in.Result)
|
||||
if err != nil {
|
||||
return challenge.VerifyChallengeOutputError
|
||||
}
|
||||
result = result[:n]
|
||||
|
||||
if len(result) < 8 {
|
||||
return challenge.VerifyChallengeOutputError
|
||||
}
|
||||
|
||||
// verify we used same challenge
|
||||
if subtle.ConstantTimeCompare(result[:len(result)-8], c) != 1 {
|
||||
return challenge.VerifyChallengeOutputFailed
|
||||
}
|
||||
|
||||
hash := sha256.Sum256(result)
|
||||
|
||||
var leadingZeroesCount int
|
||||
for i := 0; i < len(hash); i++ {
|
||||
leadingZeroes := bits.LeadingZeros8(hash[i])
|
||||
leadingZeroesCount += leadingZeroes
|
||||
if leadingZeroes < 8 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if leadingZeroesCount < int(difficulty) {
|
||||
return challenge.VerifyChallengeOutputFailed
|
||||
}
|
||||
|
||||
return challenge.VerifyChallengeOutputOK
|
||||
}, in)
|
||||
}
|
||||
BIN
embed/challenge/js-pow-sha256/runtime/runtime.wasm
Normal file
BIN
embed/challenge/js-pow-sha256/runtime/runtime.wasm
Normal file
Binary file not shown.
119
embed/challenge/js-pow-sha256/static/load.mjs
Normal file
119
embed/challenge/js-pow-sha256/static/load.mjs
Normal file
@@ -0,0 +1,119 @@
|
||||
let _worker;
|
||||
let _webWorkerURL;
|
||||
let _challenge;
|
||||
let _target;
|
||||
let _difficulty;
|
||||
|
||||
async function setup(config) {
|
||||
const { challenge, target, difficulty } = await fetch(config.Path + "/make-challenge", { method: "POST" })
|
||||
.then(r => {
|
||||
if (!r.ok) {
|
||||
throw new Error("Failed to fetch config");
|
||||
}
|
||||
return r.json();
|
||||
})
|
||||
.catch(err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
_challenge = challenge;
|
||||
_target = target;
|
||||
_difficulty = difficulty;
|
||||
|
||||
_webWorkerURL = URL.createObjectURL(new Blob([
|
||||
'(', processTask(challenge, difficulty), ')()'
|
||||
], { type: 'application/javascript' }));
|
||||
_worker = new Worker(_webWorkerURL);
|
||||
|
||||
return `Difficulty ${difficulty}`
|
||||
}
|
||||
|
||||
function challenge() {
|
||||
return new Promise((resolve, reject) => {
|
||||
_worker.onmessage = (event) => {
|
||||
_worker.terminate();
|
||||
resolve(event.data);
|
||||
};
|
||||
|
||||
_worker.onerror = (event) => {
|
||||
_worker.terminate();
|
||||
reject();
|
||||
};
|
||||
|
||||
_worker.postMessage({
|
||||
challenge: _challenge,
|
||||
target: _target,
|
||||
difficulty: _difficulty,
|
||||
});
|
||||
|
||||
URL.revokeObjectURL(_webWorkerURL);
|
||||
});
|
||||
}
|
||||
|
||||
function processTask() {
|
||||
return function () {
|
||||
|
||||
const decodeHex = (str) => {
|
||||
let result = new Uint8Array(str.length>>1)
|
||||
for (let i = 0; i < str.length; i += 2){
|
||||
result[i>>1] = parseInt(str.substring(i, i + 2), 16)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const encodeHex = (buf) => {
|
||||
return buf.reduce((a, b) => a + b.toString(16).padStart(2, '0'), '')
|
||||
}
|
||||
|
||||
const lessThan = (buf, target) => {
|
||||
for(let i = 0; i < buf.length; ++i){
|
||||
if (buf[i] < target[i]){
|
||||
return true;
|
||||
} else if (buf[i] > target[i]){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const increment = (number) => {
|
||||
for ( let i = 0; i < number.length; i++ ) {
|
||||
if(number[i]===255){
|
||||
number[i] = 0;
|
||||
} else {
|
||||
number[i]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener('message', async (event) => {
|
||||
let data = decodeHex(event.data.challenge);
|
||||
let target = decodeHex(event.data.target);
|
||||
|
||||
let nonce = new Uint8Array(8);
|
||||
let buf = new Uint8Array(data.length + nonce.length);
|
||||
buf.set(data, 0);
|
||||
|
||||
while(true) {
|
||||
buf.set(nonce, data.length);
|
||||
let result = new Uint8Array(await crypto.subtle.digest("SHA-256", buf))
|
||||
|
||||
if (lessThan(result, target)){
|
||||
const nonceNumber = Number(new BigUint64Array(nonce.buffer).at(0))
|
||||
postMessage({
|
||||
result: encodeHex(buf),
|
||||
info: `iterations ${nonceNumber}`,
|
||||
});
|
||||
return
|
||||
}
|
||||
increment(nonce)
|
||||
}
|
||||
|
||||
});
|
||||
}.toString();
|
||||
}
|
||||
|
||||
export { setup, challenge }
|
||||
Reference in New Issue
Block a user