WASM version of MemoryPoW!

This commit is contained in:
catbref 2020-05-28 14:09:53 +01:00
parent 8df3c68df9
commit 31e85226f4
7 changed files with 347 additions and 0 deletions

View File

@ -0,0 +1,28 @@
# Use GNU make
# For FreeBSD 12. Probably comment these out for Linux, MacOS, etc.
CLANG=/usr/local/llvm-devel/bin/clang
LLVM_DWARFDUMP=/usr/local/llvm-devel/bin/llvm-dwarfdump
WASM_SOURCEMAP_PY=/usr/local/lib/emscripten/tools/wasm-sourcemap.py
.PHONY: all
all: memory-pow memory-pow.wasm test.wasm
clean:
rm -f memory-pow *.wasm*
%.wasm.full: %.c Makefile
${CLANG} -g -Os --target=wasm32 --no-standard-libraries -Wl,--export-all -Wl,--no-entry -Wl,--import-memory -o $@ $<
%.wasm.dwarf: %.wasm.full
${LLVM_DWARFDUMP} $< > $@
%.wasm: %.wasm.full %.wasm.dwarf
${WASM_SOURCEMAP_PY} -w $@ --sources --prefix $(CURDIR) --source-map-url http://localhost:8080/$(@:.wasm=.wasm.map) --output $(@:.wasm=.wasm.map) --dwarfdump-output $(@:.wasm=.wasm.dwarf) $<
memory-pow: memory-pow-main.c memory-pow.c
cc -Os -g -L/usr/local/lib -o memory-pow memory-pow-main.c -lssl -lcrypto
emrun:
emrun --no_browser --port 8080 .

View File

@ -0,0 +1,59 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#define NO_STDINT_TYPEDEFS
#include "memory-pow.c"
unsigned int toInt(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return 10 + c - 'A';
if (c >= 'a' && c <= 'f') return 10 + c - 'a';
return -1;
}
void hexToRaw(char *hex, uint8_t *data, int dataLength) {
for (int i = 0; i < dataLength; ++i) {
data[i] = 16 * toInt(hex[i * 2]) + toInt(hex[i * 2 + 1]);
}
}
void digest(uint8_t *message, int messageLength, uint8_t *hash) {
EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
const EVP_MD *md = EVP_sha256();
EVP_DigestInit_ex(mdctx, md, NULL);
EVP_DigestUpdate(mdctx, message, messageLength);
EVP_DigestFinal_ex(mdctx, hash, NULL);
}
int main(int argc, char *argv[], char *env[]) {
if (argc < 2) {
fprintf(stderr, "usage: %s hex [difficulty]\n", argv[0]);
return 2;
}
int dataLength = strlen(argv[1]) / 2;
uint8_t data[dataLength];
hexToRaw(argv[1], data, dataLength);
int difficulty = 12;
if (argc > 2)
sscanf(argv[2], "%d", &difficulty);
printf("Using difficulty: %d\n", difficulty);
// Hash data with SHA256
uint8_t hash[32];
digest(data, dataLength, hash);
size_t workBufferLength = 8 * 1024 * 1024;
uint64_t *workBuffer = (uint64_t *) malloc(workBufferLength);
uint32_t nonce = compute2(hash, workBuffer, workBufferLength, difficulty);
printf("nonce: %d\n", nonce);
}

View File

@ -0,0 +1,88 @@
#ifndef NO_STDINT_TYPEDEFS
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
#endif
#define WORKBUFFER_LENGTH (longBufferLength / 8)
#define INTEGER_MAX_VALUE 0x7fffffffULL
uint64_t xoshiro256p(uint64_t state[]) {
uint64_t result = state[0] + state[3];
uint64_t temp = state[1] << 17;
state[2] ^= state[0];
state[3] ^= state[1];
state[1] ^= state[2];
state[0] ^= state[3];
state[2] ^= temp;
state[3] = (state[3] << 45) | (state[3] >> (64 - 45)); // rol64(s[3], 45);
return result;
}
int numberOfLeadingZeros32(uint32_t i) {
if (i <= 0)
return i == 0 ? 32 : 0;
int n = 31;
if (i >= 1 << 16) { n -= 16; i >>= 16; }
if (i >= 1 << 8) { n -= 8; i >>= 8; }
if (i >= 1 << 4) { n -= 4; i >>= 4; }
if (i >= 1 << 2) { n -= 2; i >>= 2; }
return n - (i >> 1);
}
int numberOfLeadingZeros64(uint64_t i) {
uint32_t x = (uint32_t) (i >> 32);
return x == 0
? 32 + numberOfLeadingZeros32((uint32_t) i)
: numberOfLeadingZeros32(x);
}
uint32_t compute2(uint8_t *hash, uint64_t *workBuffer, uint32_t workBufferLength, int difficulty) {
uint64_t longHash[4];
for (int l = 0; l < 4; ++l) {
longHash[l] = 0;
for (int b = 0; b < 8; ++b) {
longHash[l] = longHash[l] << 8 | hash[l * 8 + b];
}
}
uint32_t longBufferLength = workBufferLength / 8;
uint64_t state[4] = { 0, 0, 0, 0 };
uint64_t seed = 8682522807148012ULL;
uint64_t seedMultiplier = 1181783497276652981ULL;
// For each nonce...
uint32_t nonce = -1;
uint64_t result = 0;
do {
++nonce;
seed *= seedMultiplier; // per nonce
state[0] = longHash[0] ^ seed;
state[1] = longHash[1] ^ seed;
state[2] = longHash[2] ^ seed;
state[3] = longHash[3] ^ seed;
// Fill work buffer with random
for (uint32_t i = 0; i < WORKBUFFER_LENGTH; ++i)
workBuffer[i] = xoshiro256p(state);
// Random bounce through whole buffer
result = workBuffer[0];
for (uint32_t i = 0; i < 1024; ++i) {
uint32_t index = (uint32_t) (xoshiro256p(state) & INTEGER_MAX_VALUE) % WORKBUFFER_LENGTH;
result ^= workBuffer[index];
}
// Return if final value > difficulty
} while (numberOfLeadingZeros64(result) < difficulty);
return nonce;
}

View File

@ -0,0 +1,41 @@
<!doctype html>
<html>
<head>
<script>
var memory = new WebAssembly.Memory({ initial: 256, maximum: 256 });
var heap = new Uint8Array(memory.buffer);
var brk = 512 * 1024; // stack top
var sbrk = function(size) {
var old = brk;
brk += size;
if (brk > heap.length)
throw new Error("heap exhausted");
return old;
};
var importObject = {
env: {
memory: memory
},
};
WebAssembly.instantiateStreaming(fetch('memory-pow.wasm'), importObject)
.then(obj => {
window.instance = obj.instance;
window._compute2 = obj.instance.exports.compute2;
});
var hashPtr = sbrk(32);
var hashAry = new Uint8Array(memory.buffer, hashPtr, 32);
hashAry.set([250, 34, 223, 225, 218, 144, 19, 179, 193, 20, 80, 64, 172, 174, 144, 137, 224, 192, 139, 193, 193, 160, 113, 150, 20, 244, 183, 58, 221, 111, 110, 245]);
var workBufferLength = 8 * 1024 * 1024;
var workBufferPtr = sbrk(workBufferLength);
</script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,67 @@
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
uint32_t test() {
return 4013;
}
uint32_t test2(uint8_t values[], int count) {
uint32_t sum = 0;
for (int i = 0; i < count; ++i)
sum += values[i];
return sum;
}
void test3(uint8_t hash[], uint64_t longHash[]) {
for (int l = 0; l < 4; ++l) {
longHash[l] = 0;
for (int b = 0; b < 8; ++b)
longHash[l] = longHash[l] << 8 | hash[l * 8 + b];
}
}
void test4(uint64_t *result) {
uint64_t seed = 8682522807148012ULL;
uint64_t seedMultiplier = 1181783497276652981ULL;
*result = seed * seedMultiplier;
}
void test5(uint64_t longHash[], uint64_t state[]) {
uint64_t seed = 8682522807148012ULL;
uint64_t seedMultiplier = 1181783497276652981ULL;
seed *= seedMultiplier; // per nonce
state[0] = longHash[0] ^ seed;
state[1] = longHash[1] ^ seed;
state[2] = longHash[2] ^ seed;
state[3] = longHash[3] ^ seed;
}
uint32_t shr(uint32_t in, int shift) {
return in >> shift;
}
void xoshiro256p(uint64_t state[], uint64_t *result) {
*result = state[0] + state[3];
uint64_t temp = state[1] << 17;
state[2] ^= state[0];
state[3] ^= state[1];
state[1] ^= state[2];
state[0] ^= state[3];
state[2] ^= temp;
state[3] = (state[3] << 45) | (state[3] >> (64 - 45)); // rol64(s[3], 45);
}
void fillWorkBuffer(uint64_t workBuffer[], int workBufferLength, uint64_t state[]) {
// Fill work buffer with random
for (uint32_t i = 0; i < workBufferLength; ++i)
xoshiro256p(state, &workBuffer[i]);
}

View File

@ -0,0 +1,53 @@
<!doctype html>
<html>
<head>
<script>
var memory = new WebAssembly.Memory({ initial: 256, maximum: 256 });
var heap = new Uint8Array(memory.buffer);
var brk = 512 * 1024; // stack top
var sbrk = function(size) {
var old = brk;
brk += size;
if (brk > heap.length)
throw new Error("heap exhausted");
return old;
};
var importObject = {
env: {
memory: memory,
},
};
WebAssembly.instantiateStreaming(fetch('test.wasm'), importObject)
.then(obj => {
window.instance = obj.instance;
for (f in obj.instance.exports)
if (obj.instance.exports[f] instanceof Function && f.charAt(0) != '_')
window["_" + f] = obj.instance.exports[f];
});
var hashPtr = sbrk(32);
var hashAry = new Uint8Array(memory.buffer, hashPtr, 32);
hashAry.set([250, 34, 223, 225, 218, 144, 19, 179, 193, 20, 80, 64, 172, 174, 144, 137, 224, 192, 139, 193, 193, 160, 113, 150, 20, 244, 183, 58, 221, 111, 110, 245]);
var longHashPtr = sbrk(4 * 8);
var longHashAry = new BigUint64Array(memory.buffer, longHashPtr, 4);
var statePtr = sbrk(4 * 8);
var stateAry = new BigUint64Array(memory.buffer, statePtr, 4);
var resultPtr = sbrk(8);
var resultAry = new BigUint64Array(memory.buffer, resultPtr, 1);
var workBufferLength = 8 * 1024 * 1024;
var workBufferPtr = sbrk(workBufferLength);
var workBufferAry = new BigUint64Array(memory.buffer, workBufferPtr, workBufferLength / 8);
</script>
</head>
<body>
</body>
</html>

View File

@ -67,4 +67,15 @@ public class MemoryPoWTests {
System.out.println(String.format("Max nonce: %d", maxNonce));
}
@Test
public void testKnownCompute2() {
byte[] data = new byte[] { (byte) 0xaa, (byte) 0xbb, (byte) 0xcc };
int expectedNonce = 4013;
int nonce = MemoryPoW.compute2(data, 8 * 1024 * 1024, 12);
System.out.println(String.format("Nonce: %d", nonce));
assertEquals(expectedNonce, nonce);
}
}