diff --git a/.gitignore b/.gitignore index a5373f6..3b8da3a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,2 @@ .pio -.vscode/.browse.c_cpp.db* -.vscode/c_cpp_properties.json -.vscode/launch.json -.vscode/ipch -.vscode/* +.vscode \ No newline at end of file diff --git a/README.md b/README.md index b82062b..5535dda 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,83 @@ # NerdSoloMiner -The NerdSoloMiner v2 +**The NerdSoloMiner v2** + +This is a **free and open source project** that let you try to reach a bitcoin block with a small piece of hardware. + +The main aim of this project is to let you **learn more about minery** and to have a beautiful piece of hardware in your desktop. + Original project https://github.com/valerio-vaccaro/HAN -![image](https://raw.githubusercontent.com/BitMaker-hub/NerdMiner_v2/master/images/NerdMinerv2.jpg) +![image](images/NerdMinerv2.jpg) ## Requirements - TTGO T-Display S3 - 3D BOX [here](3d_files/) -## Description -ESP32 implementing Stratum protocol to mine on solo pool. Pool can be changed but originally works with ckpool. +### Project description +**ESP32 implementing Stratum protocol** to mine on solo pool. Pool can be changed but originally works with ckpool. This project is using ESP32-S3, uses WifiManager to modify miner settings and save them to SPIFF. This miner is multicore and multithreads, each thread mine a different block template. After 1,000,000 trials the block in refreshed in order to avoid mining on old template. -## TUTORIAL -Create your own miner using the online tool ESPtool and the binary files that you will find in the bin folder. +***Current project is still in developement and more features will be added*** + +## Build Tutorial +### Hardware requirements +- TTGO T-Display S3 > Buy it on aliexpress or amazon +- 3D BOX + +### Flash firmware +Create your own miner using the online tool **ESPtool** and the **binary files** that you will find in the src/bin folder. If you want you can compile the entire project using Arduino, PlatformIO or Expressif IDF. 1. Get a TTGO T-display S3 1. Download this repository 1. Go to ESPtool online: https://espressif.github.io/esptool-js/ -1. Load the firmware with the binaries from the bin folder. +1. Load the firmware with the binaries from the src/bin folder. +1. Plug your board and select each file from src/bin with its address -Complete tutorial on YouTube: - +#### Build troubleshooting +1. Online ESPtool works with chrome, chromium, brave +1. ESPtool recommendations: use 115200bps +1. Build errors > If during firmware download upload stops, it's recommended to enter the board in boot mode. Unplug cable, hold right bottom button and then plug cable. Try programming + +### NerdMiner configuration +After programming, you will only need to setup your Wifi and BTC address. + +1. Connect to NerdMinerAP +1. Setup your Wifi Network +1. Add your BTCaddress + +Optional you can select other pool: + +| Pool URL | Port | URL | +|--- |--- |--- | +| solo.ckpool.org | 3333 | https://solo.ckpool.org/ | +| btc.zsolo.bid | 6057 | https://zsolo.bid/en/btc-solo-mining-pool | +| eu.stratum.slushpool.com | 3333 | https://braiins.com/pool | + +**If you need to reboot your currentConfig**, hold right top button during 5 seconds and config will be deleted. + +#### Build video [![Ver video aquí](https://img.youtube.com/vi/POUT2R_opDs/0.jpg)](https://youtu.be/POUT2R_opDs) -## DEVELOPMENT -You can use platformio pltaform to develop this project. +## Developers +### Project guidelines +- Current project was addapted to work with PlatformIO +- Current project works with ESP32-S3 but any ESP32 can be used. +- Partition squeme should be build as huge app +- All libraries needed shown on platform.ini + +### On process +- [x] Move project to platformIO +- [x] Bug rectangle on screen when 1milion shares +- [x] Bug memory leaks +- [x] Bug Reboots when received JSON contains some null values +- [ ] Improve hashrate using Blockstream Jade miner code +- [ ] Add blockHeight to screen +- [ ] Add new screen with global mining stats +- [ ] Add support to control BM1397 + +Enjoy \ No newline at end of file diff --git a/TFT_setup/readme.txt b/TFT_setup/readme.txt deleted file mode 100644 index 1962079..0000000 --- a/TFT_setup/readme.txt +++ /dev/null @@ -1,2 +0,0 @@ -Navegar al directorio mis documentos, arduino y allí cambiar la configuración del fichero user_setup.h por este. -Verigicar que el user_setup_select.h está indicado que es user_setup.h \ No newline at end of file diff --git a/bin/0x0000_NerdMinerV2.ino.bootloader.bin b/bin/0x0000_NerdMinerV2.ino.bootloader.bin deleted file mode 100644 index bd34cd9..0000000 Binary files a/bin/0x0000_NerdMinerV2.ino.bootloader.bin and /dev/null differ diff --git a/bin/0x0000_bootloader.bin b/bin/0x0000_bootloader.bin new file mode 100644 index 0000000..96570fc Binary files /dev/null and b/bin/0x0000_bootloader.bin differ diff --git a/bin/0x10000_NerdMinerV2.ino.bin b/bin/0x10000_NerdMinerV2.ino.bin deleted file mode 100644 index a8c7a39..0000000 Binary files a/bin/0x10000_NerdMinerV2.ino.bin and /dev/null differ diff --git a/bin/0x10000_firmware.bin b/bin/0x10000_firmware.bin new file mode 100644 index 0000000..a268a1d Binary files /dev/null and b/bin/0x10000_firmware.bin differ diff --git a/bin/0x8000_NerdMinerV2.ino.partitions.bin b/bin/0x8000_partitions.bin similarity index 100% rename from bin/0x8000_NerdMinerV2.ino.partitions.bin rename to bin/0x8000_partitions.bin diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/platformio.ini b/platformio.ini index a56362c..6273969 100644 --- a/platformio.ini +++ b/platformio.ini @@ -7,6 +7,7 @@ ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html + [platformio] globallib_dir = lib @@ -29,7 +30,8 @@ build_flags = -D BOARD_HAS_PSRAM -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 + -D DEBUG_MINING=1 lib_deps = https://github.com/takkaO/OpenFontRender - bblanchon/ArduinoJson@^6.21.1 - https://github.com/tzapu/WiFiManager.git + bblanchon/ArduinoJson@^6.21.2 + https://github.com/tzapu/WiFiManager.git \ No newline at end of file diff --git a/src/mining.cpp b/src/mining.cpp index 65ada10..2420e07 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -14,6 +14,7 @@ unsigned long templates = 0; unsigned long hashes= 0; unsigned long Mhashes = 0; + int halfshares; // increase if blockhash has 16 bits of zeroes int shares; // increase if blockhash has 32 bits of zeroes int valids; // increased if blockhash <= target @@ -34,6 +35,14 @@ bool checkHalfShare(unsigned char* hash) { break; } } + #ifdef DEBUG_MINING + if (valid) { + Serial.print("\thalf share : "); + for (size_t i = 0; i < 32; i++) + Serial.printf("%02x ", hash[i]); + Serial.println(); + } + #endif return valid; } @@ -45,6 +54,14 @@ bool checkShare(unsigned char* hash) { break; } } + #ifdef DEBUG_MINING + if (valid) { + Serial.print("\tshare : "); + for (size_t i = 0; i < 32; i++) + Serial.printf("%02x ", hash[i]); + Serial.println(); + } + #endif return valid; } @@ -59,12 +76,14 @@ bool checkValid(unsigned char* hash, unsigned char* target) { break; } } + #ifdef DEBUG_MINING if (valid) { Serial.print("\tvalid : "); for (size_t i = 0; i < 32; i++) Serial.printf("%02x ", hash[i]); Serial.println(); } + #endif return valid; } @@ -141,12 +160,14 @@ void runWorker(void *name) { Serial.print(" extranonce2_size: "); Serial.println(extranonce2_size); Serial.print(" error: "); Serial.println(error); if((extranonce1.length() == 0) || line.length() == 0 || (error != 0)) { - Serial.printf("[WORKER] %s >>>>>>>>> Worker aborted\n", (char *)name); + Serial.printf("[WORKER] %s >>>>>>>>> Work aborted\n", (char *)name); Serial.printf("extranonce1 length: %u | line2 length: %u | error code: %u \n", extranonce1.length(), line.length(), error); client.stop(); + doc.clear(); + doc.garbageCollect(); continue; } @@ -160,6 +181,7 @@ void runWorker(void *name) { Serial.print(" Receiving: "); Serial.println(client.readStringUntil('\n')); Serial.print(" Receiving: "); Serial.println(client.readStringUntil('\n')); client.stop(); + deserializeJson(doc, line); String job_id = String((const char*) doc["params"][0]); String prevhash = String((const char*) doc["params"][1]); @@ -170,6 +192,8 @@ void runWorker(void *name) { String nbits = String((const char*) doc["params"][6]); String ntime = String((const char*) doc["params"][7]); bool clean_jobs = doc["params"][8]; //bool + + #ifdef DEBUG_MINING Serial.print(" job_id: "); Serial.println(job_id); Serial.print(" prevhash: "); Serial.println(prevhash); Serial.print(" coinb1: "); Serial.println(coinb1); @@ -179,6 +203,7 @@ void runWorker(void *name) { Serial.print(" nbits: "); Serial.println(nbits); Serial.print(" ntime: "); Serial.println(ntime); Serial.print(" clean_jobs: "); Serial.println(clean_jobs); + #endif doc.clear(); templates++; @@ -223,24 +248,26 @@ void runWorker(void *name) { for (int k = 0; k < pad; k++) { extranonce2_b_char[k] = '0'; } + extranonce2_b_char[pad+1] = 0; extranonce2_b = String(extranonce2_b_char) + extranonce2_b; String extranonce2 = String(extranonce2_a + extranonce2_b).substring(0, 17 - (2 * extranonce2_size)); - Serial.print(" extranonce2: "); Serial.println(extranonce2); - //get coinbase - coinbase_hash_bin = hashlib.sha256(hashlib.sha256(binascii.unhexlify(coinbase)).digest()).digest() String coinbase = coinb1 + extranonce1 + extranonce2 + coinb2; - Serial.print(" coinbase: "); Serial.println(coinbase); size_t str_len = coinbase.length()/2; uint8_t bytearray[str_len]; size_t res = to_byte_array(coinbase.c_str(), str_len*2, bytearray); - Serial.print(" coinbase bytes - size "); - Serial.println(res); + + #ifdef DEBUG_MINING + Serial.print(" extranonce2: "); Serial.println(extranonce2); + Serial.print(" coinbase: "); Serial.println(coinbase); + Serial.print(" coinbase bytes - size: "); Serial.println(res); for (size_t i = 0; i < res; i++) Serial.printf("%02x ", bytearray[i]); Serial.println("---"); + #endif mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256; mbedtls_md_init(&ctx); @@ -257,10 +284,12 @@ void runWorker(void *name) { mbedtls_md_update(&ctx, interResult, 32); mbedtls_md_finish(&ctx, shaResult); + #ifdef DEBUG_MINING Serial.print(" coinbase double sha: "); for (size_t i = 0; i < 32; i++) Serial.printf("%02x", shaResult[i]); Serial.println(""); + #endif byte merkle_result[32]; // copy coinbase hash @@ -272,16 +301,22 @@ void runWorker(void *name) { uint8_t bytearray[32]; size_t res = to_byte_array(merkle_element, 64, bytearray); - // Serial.print("\tmerkle element "); Serial.print(k); Serial.print(": "); Serial.println(merkle_element); + #ifdef DEBUG_MINING + Serial.print(" merkle element "); Serial.print(k); Serial.print(": "); Serial.println(merkle_element); + #endif for (size_t i = 0; i < 32; i++) { merkle_concatenated[i] = merkle_result[i]; merkle_concatenated[32 + i] = bytearray[i]; } - // Serial.print("\tmerkle concatenated: "); - // for (size_t i = 0; i < 64; i++) - // Serial.printf("%02x", merkle_concatenated[i]); - // Serial.println(""); - + + #ifdef DEBUG_MINING + Serial.print(" merkle element "); Serial.print(k); Serial.print(": "); Serial.println(merkle_element); + Serial.print(" merkle concatenated: "); + for (size_t i = 0; i < 64; i++) + Serial.printf("%02x", merkle_concatenated[i]); + Serial.println(""); + #endif + mbedtls_md_starts(&ctx); mbedtls_md_update(&ctx, merkle_concatenated, 64); mbedtls_md_finish(&ctx, interResult); @@ -290,14 +325,16 @@ void runWorker(void *name) { mbedtls_md_update(&ctx, interResult, 32); mbedtls_md_finish(&ctx, merkle_result); - // Serial.print("\tmerkle sha : "); - // for (size_t i = 0; i < 32; i++) - // Serial.printf("%02x", merkle_result[i]); - // Serial.println(""); + #ifdef DEBUG_MINING + Serial.print(" merkle sha : "); + for (size_t i = 0; i < 32; i++) + Serial.printf("%02x", merkle_result[i]); + Serial.println(""); + #endif } // merkle root from merkle_result - Serial.print("\tmerkle sha : "); + Serial.print(" merkle sha : "); char merkle_root[65]; for (int i = 0; i < 32; i++) { Serial.printf("%02x", merkle_result[i]); @@ -310,9 +347,11 @@ void runWorker(void *name) { String blockheader = version + prevhash + String(merkle_root) + nbits + ntime + "00000000"; str_len = blockheader.length()/2; uint8_t bytearray_blockheader[str_len]; - Serial.println(" blockheader bytes "); Serial.print(str_len); Serial.print(" -> "); res = to_byte_array(blockheader.c_str(), str_len*2, bytearray_blockheader); - Serial.println(res); + + #ifdef DEBUG_MINING + Serial.println(" blockheader bytes "); Serial.print(str_len); Serial.print(" -> "); + #endif // reverse version uint8_t buff; @@ -342,38 +381,42 @@ void runWorker(void *name) { bytearray_blockheader[2 * boffset + bsize - 1 - j] = buff; } + + #ifdef DEBUG_MINING Serial.print(" >>> bytearray_blockheader : "); for (size_t i = 0; i < 4; i++) - Serial.printf("%02x ", bytearray_blockheader[i]); + Serial.printf("%02x", bytearray_blockheader[i]); Serial.println(""); Serial.println("version"); for (size_t i = 0; i < 4; i++) - Serial.printf("%02x ", bytearray_blockheader[i]); + Serial.printf("%02x", bytearray_blockheader[i]); Serial.println(""); Serial.println("prev hash"); for (size_t i = 4; i < 4+32; i++) - Serial.printf("%02x ", bytearray_blockheader[i]); + Serial.printf("%02x", bytearray_blockheader[i]); Serial.println(""); Serial.println("merkle root"); for (size_t i = 36; i < 36+32; i++) - Serial.printf("%02x ", bytearray_blockheader[i]); + Serial.printf("%02x", bytearray_blockheader[i]); Serial.println(""); Serial.println("time"); for (size_t i = 68; i < 68+4; i++) - Serial.printf("%02x ", bytearray_blockheader[i]); + Serial.printf("%02x", bytearray_blockheader[i]); Serial.println(""); Serial.println("difficulty"); for (size_t i = 72; i < 72+4; i++) - Serial.printf("%02x ", bytearray_blockheader[i]); + Serial.printf("%02x", bytearray_blockheader[i]); Serial.println(""); Serial.println("nonce"); for (size_t i = 76; i < 76+4; i++) - Serial.printf("%02x ", bytearray_blockheader[i]); + Serial.printf("%02x", bytearray_blockheader[i]); Serial.println(""); + #endif // search a valid nonce uint32_t nonce = 0; uint32_t startT = micros(); + Serial.println(">>> STARTING TO HASH NONCES"); while(true) { bytearray_blockheader[76] = (nonce >> 0) & 0xFF; bytearray_blockheader[77] = (nonce >> 8) & 0xFF; @@ -395,33 +438,33 @@ void runWorker(void *name) { // check if share if(checkShare(shaResult)) { shares++; - } - } - - // check if valid header - if(checkValid(shaResult, bytearray_target)) { - //Serial.printf("%s on core %d: ", (char *)name, xPortGetCoreID()); - Serial.printf("[WORKER] %s CONGRATULATIONS! Valid completed with nonce: %d | 0x%x\n", (char *)name, nonce, nonce); - valids++; - Serial.printf("[WORKER] %s Submiting work valid!\n", (char *)name); - while (!client.connected()) { - client.connect(poolString, portNumber); - vTaskDelay(1000 / portTICK_PERIOD_MS); - } - // STEP 3: Submit mining job - payload = "{\"params\": [\"" + ADDRESS + "\", \"" + job_id + "\", \"" + extranonce2 + "\", \"" + ntime + "\", \"" + String(nonce, HEX) + "\"], \"id\": "+ String(id++) +", \"method\": \"mining.submit\"}"; - Serial.print(" Sending : "); Serial.println(payload); - client.print(payload.c_str()); - line = client.readStringUntil('\n'); - Serial.print(" Receiving: "); Serial.println(line); - client.stop(); - // exit - nonce = MAX_NONCE; - break; - } + // check if valid header + if(checkValid(shaResult, bytearray_target)) { + //Serial.printf("%s on core %d: ", (char *)name, xPortGetCoreID()); + Serial.printf("[WORKER] %s CONGRATULATIONS! Valid completed with nonce: %d | 0x%x\n", (char *)name, nonce, nonce); + valids++; + Serial.printf("[WORKER] %s Submiting work valid!\n", (char *)name); + while (!client.connected()) { + client.connect(poolString, portNumber); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + // STEP 3: Submit mining job + payload = "{\"params\": [\"" + ADDRESS + "\", \"" + job_id + "\", \"" + extranonce2 + "\", \"" + ntime + "\", \"" + String(nonce, HEX) + "\"], \"id\": "+ String(id++) +", \"method\": \"mining.submit\"}"; + Serial.print(" Sending : "); Serial.println(payload); + client.print(payload.c_str()); + line = client.readStringUntil('\n'); + Serial.print(" Receiving: "); Serial.println(line); + client.stop(); + // exit + nonce = MAX_NONCE; + break; + } + } + } nonce++; hashes++; + if(hashes++>1000000) { Mhashes++; hashes=0;} // exit if (nonce >= MAX_NONCE) { diff --git a/src/wManager.h b/src/wManager.h index b7c1ad1..6eafb9c 100644 --- a/src/wManager.h +++ b/src/wManager.h @@ -1,5 +1,3 @@ - - //Botón configuración #define PIN_BUTTON_1 0 #define PIN_BUTTON_2 14 @@ -8,3 +6,4 @@ void init_WifiManager(); void wifiManagerProcess(); void checkResetConfigButton(); void checkRemoveConfiguration(); + diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html