diff --git a/src/main/wasm/memory-pow/Makefile b/src/main/wasm/memory-pow/Makefile new file mode 100644 index 00000000..6d1f0a4c --- /dev/null +++ b/src/main/wasm/memory-pow/Makefile @@ -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 . diff --git a/src/main/wasm/memory-pow/memory-pow-main.c b/src/main/wasm/memory-pow/memory-pow-main.c new file mode 100644 index 00000000..3b5043bc --- /dev/null +++ b/src/main/wasm/memory-pow/memory-pow-main.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include + +#include + +#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); + } diff --git a/src/main/wasm/memory-pow/memory-pow.c b/src/main/wasm/memory-pow/memory-pow.c new file mode 100644 index 00000000..e8ac45c2 --- /dev/null +++ b/src/main/wasm/memory-pow/memory-pow.c @@ -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; +} diff --git a/src/main/wasm/memory-pow/memory-pow.html b/src/main/wasm/memory-pow/memory-pow.html new file mode 100644 index 00000000..9dc43301 --- /dev/null +++ b/src/main/wasm/memory-pow/memory-pow.html @@ -0,0 +1,41 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/wasm/memory-pow/test.c b/src/main/wasm/memory-pow/test.c new file mode 100644 index 00000000..5b3d7b97 --- /dev/null +++ b/src/main/wasm/memory-pow/test.c @@ -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]); +} diff --git a/src/main/wasm/memory-pow/test.html b/src/main/wasm/memory-pow/test.html new file mode 100644 index 00000000..383f0c54 --- /dev/null +++ b/src/main/wasm/memory-pow/test.html @@ -0,0 +1,53 @@ + + + + + + + + \ No newline at end of file diff --git a/src/test/java/org/qortal/test/MemoryPoWTests.java b/src/test/java/org/qortal/test/MemoryPoWTests.java index 6232ae06..1e7f8946 100644 --- a/src/test/java/org/qortal/test/MemoryPoWTests.java +++ b/src/test/java/org/qortal/test/MemoryPoWTests.java @@ -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); + } + }