/** * */ #include #include #include #include #include #include "ee382n_bitcoin/bitcoin.h" #include "ee382n_bitcoin/sha256.h" #include "ee382n_bitcoin/sha256-arm.h" #include #include #include #include #include #include #include #include #include #define MAP_SIZE 4096UL #define MAP_MASK (MAP_SIZE - 1) #define MINE_CORE 0x80000000 // Add the correct physical address here // typedef struct BITCOIN_BLOCK_HEADER { // uint8_t version[4]; // uint8_t prev_block[32]; // uint8_t merkle_root[32]; // uint8_t timestamp[4]; // uint8_t difficulty_target[4]; // uint8_t nonce[4]; // } bitcoin_block_header_t; // Initialize the genesis block bitcoin_block_header_t genesis = { .version = {0x01, 0x00, 0x00, 0x00}, .prev_block = {0}, .merkle_root = { 0x3B, 0xA3, 0xED, 0xFD, 0x7A, 0x7B, 0x12, 0xB2, 0x7A, 0xC7, 0x2C, 0x3E, 0x67, 0x76, 0x8F, 0x61, 0x7F, 0xC8, 0x1B, 0xC3, 0x88, 0x8A, 0x51, 0x32, 0x3A, 0x9F, 0xB8, 0xAA, 0x4B, 0x1E, 0x5E, 0x4A }, .timestamp = {0x29, 0xAB, 0x5F, 0x49}, //.difficulty_target = {0xcb, 0x04, 0x04, 0x1b}, //.difficulty_target = {0xFF, 0xFF, 0x00, 0x1D}, .difficulty_target = {0xFF, 0xFF, 0xFF, 0x1D}, .nonce = {0x00, 0x00, 0x00, 0x00} //.nonce = {0x1D, 0xAC, 0x2B, 0x7C} }; void expand_difficulty(const uint8_t *compact, uint32_t *expanded) { uint32_t exponent = compact[3]; uint32_t coeff = (compact[2] << 16) | (compact[1] << 8) | compact[0]; // Clear all 8 dwords (256 bits) memset(expanded, 0, 32); //printf("Exponent: 0x%02x\n", exponent); //printf("Coefficient: 0x%06x\n", coeff); if (exponent <= 3) { coeff >>= 8 * (3 - exponent); //printf("Adjusted Coefficient for exponent <= 3: 0x%06x\n", coeff); // Place coefficient directly in the first 32-bit integer (little-endian) memcpy(expanded, &coeff, sizeof(coeff)); } else { int shift = (exponent - 3) * 8; // Calculate the total number of bits to shift int dwords_to_shift = shift / 32; // Determine how many whole dwords to shift int byte_shift = shift % 32; // Determine the remaining bits to shift //printf("Total bit shift: %d\n", shift); //printf("Dwords to shift: %d\n", dwords_to_shift); //printf("Byte shift within dword: %d\n", byte_shift); // Check if the shift stays within the bounds of the expanded array if (dwords_to_shift < 8) { expanded[dwords_to_shift] = coeff << byte_shift; //printf("Expanded[%d]: 0x%08x\n", dwords_to_shift, expanded[dwords_to_shift]); if (dwords_to_shift + 1 < 8 && byte_shift != 0) { expanded[dwords_to_shift + 1] = coeff >> (32 - byte_shift); //printf("Expanded[%d]: 0x%08x (carry-over)\n", dwords_to_shift + 1, expanded[dwords_to_shift + 1]); } } else { //printf("Shift exceeds the size of the expanded array.\n"); } } } void write_data_to_hardware(volatile uint32_t *header_base, bitcoin_block_header_t *genesis) { uint32_t *dest = (uint32_t *)header_base; uint32_t *src = (uint32_t *)genesis; size_t size = sizeof(bitcoin_block_header_t); // Handling difficulty target volatile uint32_t *Difficulty = header_base + 21; // Assuming Difficulty register starts at header_base + 22 uint32_t expanded_difficulty[8]; expand_difficulty(genesis->difficulty_target, expanded_difficulty); printf("Difficulty target:\n0x"); // Copy expanded difficulty to registers for (int i = 0; i < 8; i++) { //printf("%i, %i\n", expanded_difficulty[i], i); Difficulty[i] = expanded_difficulty[7-i]; printf("%08X", expanded_difficulty[7-i]); } printf("\n"); // Handle block header for (size_t i = 0; i < size/4; i++) { uint32_t value = src[i]; uint32_t reversed_value = ((value >> 24) & 0xff) | ((value << 8) & 0xff0000) | ((value >> 8) & 0xff00) | ((value << 24) & 0xff000000); dest[i] = reversed_value; } } double time_diff(struct timespec start, struct timespec end) { return (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1.0e9; } int main(int argc, char * argv[]) { volatile uint32_t *mapped_base; int fd; fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd == -1) { perror("Error opening /dev/mem"); return -1; } mapped_base = (volatile uint32_t*) mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, MINE_CORE); if (mapped_base == MAP_FAILED) { perror("Error mapping memory"); close(fd); return -1; } volatile uint32_t* Control = mapped_base; volatile uint32_t* Header = mapped_base + 1; volatile uint32_t* nonce = mapped_base + 21; volatile uint32_t* Difficulty = mapped_base + 22; *Control = 0; // Reset device *Control |= (1 << 4); // Set bit 4 *Control &= ~(1 << 4); // Reset bit 4 // Copy the genesis block header into the Header registers //memcpy((void*)Header, &genesis, sizeof(genesis)); write_data_to_hardware(Header, &genesis); struct timespec start_time, current_time; clock_gettime(CLOCK_MONOTONIC, &start_time); // Start timer // set bit 0 to start mining *Control |= (1 << 0); char wheel[] = {'|', '/', '-', '\\'}; int wheelIndex = 0; // Poll for results while (1) { if (*Control & (1 << 8)) { clock_gettime(CLOCK_MONOTONIC, ¤t_time); // Get current time double elapsed_time = time_diff(start_time, current_time); printf("\rFound valid nonce: 0x%x - Time taken: %.2f seconds\n", *nonce, elapsed_time); break; } else if (*Control & (1 << 12)) { clock_gettime(CLOCK_MONOTONIC, ¤t_time); // Get current time double elapsed_time = time_diff(start_time, current_time); printf("\rExhausted possible nonce values - Time taken: %.2f seconds\n", elapsed_time); return 0; } clock_gettime(CLOCK_MONOTONIC, ¤t_time); // Update current time double elapsed_time = time_diff(start_time, current_time); printf("\rProcessing: %c - Time elapsed: %.2f seconds", wheel[wheelIndex], elapsed_time); fflush(stdout); // Update the spinner index wheelIndex = (wheelIndex + 1) % 4; usleep(100000); // Sleep for 100 milliseconds } memcpy(genesis.nonce, (const void*)nonce, sizeof(uint32_t)); // Assuming nonce is a pointer to a uint32_t uint32_t original_nonce = *nonce; // Dereference the pointer to get the value // Reverse the byte order genesis.nonce[0] = (original_nonce >> 24) & 0xFF; // Move the first byte to the last genesis.nonce[1] = (original_nonce >> 16) & 0xFF; // Move the second byte to the third genesis.nonce[2] = (original_nonce >> 8) & 0xFF; // Move the third byte to the second genesis.nonce[3] = original_nonce & 0xFF; const uint8_t *byte_ptr = (const uint8_t *)&genesis; printf("Resulting header to hash:\n0x"); for (size_t i = 0; i < sizeof(bitcoin_block_header_t); i++) { printf("%02X", byte_ptr[i]); } printf("\n"); BYTE buf[SHA256_BLOCK_SIZE]; SHA256_CTX ctx; sha256_init(&ctx); sha256_update(&ctx, (const unsigned char *)&genesis, sizeof(bitcoin_block_header_t)); sha256_final(&ctx, buf); printf("Intermediate hash:\n0x"); for (int i = SHA256_BLOCK_SIZE - 1; i >= 0; i--) { printf("%02X", buf[i]); } printf("\n"); sha256_init(&ctx); sha256_update(&ctx, (const unsigned char *)&buf, SHA256_BLOCK_SIZE); sha256_final(&ctx, buf); printf("Final hash:\n0x"); for (int i = SHA256_BLOCK_SIZE - 1; i >= 0; i--) { printf("%02X", buf[i]); } printf("\n"); // Clean up munmap((void*)mapped_base, MAP_SIZE); close(fd); return 0; } int sha256_test() { BYTE text1[] = {"abc"}; BYTE text2[] = {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}; BYTE text3[] = {"aaaaaaaaaa"}; BYTE hash0[SHA256_BLOCK_SIZE] = { 0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24, 0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55}; BYTE hash1[SHA256_BLOCK_SIZE] = { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; BYTE hash2[SHA256_BLOCK_SIZE] = { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1}; BYTE hash3[SHA256_BLOCK_SIZE] = { 0xcd, 0xc7, 0x6e, 0x5c, 0x99, 0x14, 0xfb, 0x92, 0x81, 0xa1, 0xc7, 0xe2, 0x84, 0xd7, 0x3e, 0x67, 0xf1, 0x80, 0x9a, 0x48, 0xa4, 0x97, 0x20, 0x0e, 0x04, 0x6d, 0x39, 0xcc, 0xc7, 0x11, 0x2c, 0xd0}; BYTE buf[SHA256_BLOCK_SIZE]; SHA256_CTX ctx; int idx; int pass = 1; sha256_init(&ctx); sha256_update(&ctx, text1, strlen(text1)); sha256_final(&ctx, buf); pass = pass && !memcmp(hash1, buf, SHA256_BLOCK_SIZE); sha256_wiki_single(text1, strlen(text1), buf); pass = pass && !memcmp(hash1, buf, SHA256_BLOCK_SIZE); sha256_init(&ctx); sha256_update(&ctx, text2, strlen(text2)); sha256_final(&ctx, buf); pass = pass && !memcmp(hash2, buf, SHA256_BLOCK_SIZE); sha256_wiki_single(text2, strlen(text2), buf); pass = pass && !memcmp(hash2, buf, SHA256_BLOCK_SIZE); sha256_init(&ctx); for (idx = 0; idx < 100000; ++idx) sha256_update(&ctx, text3, strlen(text3)); sha256_final(&ctx, buf); pass = pass && !memcmp(hash3, buf, SHA256_BLOCK_SIZE); // Block data from https://en.bitcoin.it/wiki/Genesis_block // Could also get from a blockchain explorer bitcoin_block_header_t genesis = { .version = {01, 00, 00, 00}, .prev_block = {0}, .merkle_root = {0x3B, 0xA3, 0xED, 0xFD, 0x7A, 0x7B, 0x12, 0xB2, 0x7A, 0xC7, 0x2C, 0x3E, 0x67, 0x76, 0x8F, 0x61, 0x7F, 0xC8, 0x1B, 0xC3, 0x88, 0x8A, 0x51, 0x32, 0x3A, 0x9F, 0xB8, 0xAA, 0x4B, 0x1E, 0x5E, 0x4A}, .timestamp = {0x29, 0xAB, 0x5F, 0x49}, .difficulty_target = {0xFF, 0xFF, 0x00, 0x1D}, .nonce = {0x1D, 0xAC, 0x2B, 0x7C}}; // Test hashing bitcoin block with SHA-256 algorithm two times BYTE genesis_hash[SHA256_BLOCK_SIZE] = { 0x6F, 0xE2, 0x8C, 0x0A, 0xB6, 0xF1, 0xB3, 0x72, 0xC1, 0xA6, 0xA2, 0x46, 0xAE, 0x63, 0xF7, 0x4F, 0x93, 0x1E, 0x83, 0x65, 0xE1, 0x5A, 0x08, 0x9C, 0x68, 0xD6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00}; sha256_init(&ctx); sha256_update(&ctx, (const unsigned char *)&genesis, sizeof(bitcoin_block_header_t)); sha256_final(&ctx, buf); printf("Bitcoin genesis block intermediate hash:\n0x"); for (int i = SHA256_BLOCK_SIZE; i > 0; i--) { printf("%02X", buf[i-1]); } printf("\n"); sha256_init(&ctx); sha256_update(&ctx, (const unsigned char *)&buf, SHA256_BLOCK_SIZE); sha256_final(&ctx, buf); printf("Bitcoin genesis block hash:\n0x"); for (int i = SHA256_BLOCK_SIZE; i > 0; i--) { printf("%02X", buf[i-1]); } printf("\n"); pass = pass && !memcmp(genesis_hash, buf, SHA256_BLOCK_SIZE); // Test hashing with SHA-256 Double function memset(buf, 0, sizeof(buf)); sha256_double((const unsigned char *)&genesis, sizeof(bitcoin_block_header_t), buf); pass = pass && !memcmp(genesis_hash, buf, SHA256_BLOCK_SIZE); // Test with Wikipedia pseudocode-based implementation memset(buf, 0, sizeof(buf)); sha256_wiki_double((const unsigned char *)&genesis, sizeof(bitcoin_block_header_t), buf); pass = pass && !memcmp(genesis_hash, buf, SHA256_BLOCK_SIZE); // Test with arm ISA implementation /* empty message with padding */ uint8_t message[64]; memset(message, 0x00, sizeof(message)); message[0] = 0x80; // printf("Message:\n"); // for (int i = 0; i < sizeof(message); i++) { // printf("%02X ", message[i]); // } // printf("\n"); /* initial state */ uint32_t state[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }; // printf("Initial state:\n"); // for (int i = 0; i < 8; i++) { // printf("state[%d] = %08X\n", i, state[i]); // } sha256_process_arm(state, message, sizeof(message)); // Processing empty message // Convert state to bytes for comparison const uint8_t b1 = (uint8_t)(state[0] >> 24); const uint8_t b2 = (uint8_t)(state[0] >> 16); const uint8_t b3 = (uint8_t)(state[0] >> 8); const uint8_t b4 = (uint8_t)(state[0] >> 0); const uint8_t b5 = (uint8_t)(state[1] >> 24); const uint8_t b6 = (uint8_t)(state[1] >> 16); const uint8_t b7 = (uint8_t)(state[1] >> 8); const uint8_t b8 = (uint8_t)(state[1] >> 0); /* e3b0c44298fc1c14... */ // printf("SHA256 hash of empty message: "); // printf("%02X%02X%02X%02X%02X%02X%02X%02X...\n", // b1, b2, b3, b4, b5, b6, b7, b8); int success = ((b1 == 0xE3) && (b2 == 0xB0) && (b3 == 0xC4) && (b4 == 0x42) && (b5 == 0x98) && (b6 == 0xFC) && (b7 == 0x1C) && (b8 == 0x14)); pass = pass && success; // Test with ARM implementation on text1, text2, text3 const char *texts[] = {"", text1, text2}; const size_t text_lengths[] = {0, strlen(text1), strlen(text2)}; const BYTE *expected_hashes[] = {hash0, hash1, hash2}; for (int idx = 0; idx < 3; idx++) { sha256_arm(texts[idx], text_lengths[idx], buf); // Process each text string pass = pass && !memcmp(expected_hashes[idx], buf, SHA256_BLOCK_SIZE); // printf("Testing text%u: %s\n", idx + 1, texts[idx]); // printf("Expected: "); // for (int i = 0; i < SHA256_BLOCK_SIZE; i++) printf("%02X", expected_hashes[idx][i]); // printf("\nComputed: "); // for (int i = 0; i < SHA256_BLOCK_SIZE; i++) printf("%02X", buf[i]); // printf("\n\n"); } // Double SHA-256 using ARM sha256_double_arm((const uint8_t *)&genesis, sizeof(genesis),buf); pass = pass && !memcmp(genesis_hash, buf, SHA256_BLOCK_SIZE); // printf("Testing double SHA256 on Genesis block\nExpected: "); // for (int i = 0; i < SHA256_BLOCK_SIZE; i++) printf("%02X", genesis_hash[i]); // printf("\nComputed: "); // for (int i = 0; i < SHA256_BLOCK_SIZE; i++) printf("%02X", buf[i]); // printf("\n"); return (pass); } int sha256_benchmark() { BYTE buf[SHA256_BLOCK_SIZE]; // Block data from https://en.bitcoin.it/wiki/Genesis_block // Could also get from a blockchain explorer bitcoin_block_header_t block = { .version = {01, 00, 00, 00}, .prev_block = {0}, .merkle_root = {0x3B, 0xA3, 0xED, 0xFD, 0x7A, 0x7B, 0x12, 0xB2, 0x7A, 0xC7, 0x2C, 0x3E, 0x67, 0x76, 0x8F, 0x61, 0x7F, 0xC8, 0x1B, 0xC3, 0x88, 0x8A, 0x51, 0x32, 0x3A, 0x9F, 0xB8, 0xAA, 0x4B, 0x1E, 0x5E, 0x4A}, .timestamp = {0x29, 0xAB, 0x5F, 0x49}, .difficulty_target = {0xFF, 0xFF, 0x00, 0x1D}, .nonce = {0x1D, 0xAC, 0x2B, 0x7C}}; clock_t begin = clock(); int count; for(count = 0; count < 1000000; count++) { sha256_double((const unsigned char *)&block, sizeof(bitcoin_block_header_t), buf); // TODO: Check hash validity (bits/difficulty field) // https://bitcoin.stackexchange.com/questions/58407/what-is-most-efficient-way-to-validate-bitcoin-target-difficulty-value-in-c // Increment nonce *((uint32_t *)&(block.nonce)) = *((uint32_t *)&(block.nonce)) + 1; } clock_t end = clock(); double time_spent = (double)(end - begin) / CLOCKS_PER_SEC; printf("[sha256_double] %d double-SHA256 hashes in %f seconds => %f hashes/sec\n", count, time_spent, (double)count/time_spent); } int sha256_wiki_benchmark() { BYTE buf[SHA256_BLOCK_SIZE]; // Block data from https://en.bitcoin.it/wiki/Genesis_block // Could also get from a blockchain explorer bitcoin_block_header_t block = { .version = {01, 00, 00, 00}, .prev_block = {0}, .merkle_root = {0x3B, 0xA3, 0xED, 0xFD, 0x7A, 0x7B, 0x12, 0xB2, 0x7A, 0xC7, 0x2C, 0x3E, 0x67, 0x76, 0x8F, 0x61, 0x7F, 0xC8, 0x1B, 0xC3, 0x88, 0x8A, 0x51, 0x32, 0x3A, 0x9F, 0xB8, 0xAA, 0x4B, 0x1E, 0x5E, 0x4A}, .timestamp = {0x29, 0xAB, 0x5F, 0x49}, .difficulty_target = {0xFF, 0xFF, 0x00, 0x1D}, .nonce = {0x1D, 0xAC, 0x2B, 0x7C}}; clock_t begin = clock(); int count; for(count = 0; count < 1000000; count++) { sha256_wiki_double((const unsigned char *)&block, sizeof(bitcoin_block_header_t), buf); // TODO: Check hash validity (bits/difficulty field) // https://bitcoin.stackexchange.com/questions/58407/what-is-most-efficient-way-to-validate-bitcoin-target-difficulty-value-in-c // Increment nonce *((uint32_t *)&(block.nonce)) = *((uint32_t *)&(block.nonce)) + 1; } clock_t end = clock(); double time_spent = (double)(end - begin) / CLOCKS_PER_SEC; printf("[sha256_wiki_double] %d double-SHA256 hashes in %f seconds => %f hashes/sec\n", count, time_spent, (double)count/time_spent); } int sha256_arm_benchmark() { BYTE buf[SHA256_BLOCK_SIZE]; // Temporary buffer for hashing, though not directly used here // Block data for testing purposes bitcoin_block_header_t block = { .version = {01, 00, 00, 00}, .prev_block = {0}, .merkle_root = {0x3B, 0xA3, 0xED, 0xFD, 0x7A, 0x7B, 0x12, 0xB2, 0x7A, 0xC7, 0x2C, 0x3E, 0x67, 0x76, 0x8F, 0x61, 0x7F, 0xC8, 0x1B, 0xC3, 0x88, 0x8A, 0x51, 0x32, 0x3A, 0x9F, 0xB8, 0xAA, 0x4B, 0x1E, 0x5E, 0x4A}, .timestamp = {0x29, 0xAB, 0x5F, 0x49}, .difficulty_target = {0xFF, 0xFF, 0x00, 0x1D}, .nonce = {0x1D, 0xAC, 0x2B, 0x7C} }; uint32_t state[8]; // State for SHA256 sha256_init_arm(state); // Initialize state before hashing clock_t begin = clock(); int count; for (count = 0; count < 1000000; count++) { // Perform double hashing on the block header sha256_double_arm((const uint8_t *)&block, sizeof(bitcoin_block_header_t),buf); // Increment nonce for each hash, simulating a mining process *((uint32_t *)&(block.nonce)) = *((uint32_t *)&(block.nonce)) + 1; } clock_t end = clock(); double time_spent = (double)(end - begin) / CLOCKS_PER_SEC; printf("[sha256_double_arm] %d double-SHA256 hashes in %f seconds => %f hashes/sec\n", count, time_spent, (double)count / time_spent); return 0; // Ensure the benchmark function returns an int type } int cpuHashMain() { printf("SHA-256 tests: %s\n", sha256_test() ? "SUCCEEDED" : "FAILED"); printf("Starting sha256_benchmark.\n"); sha256_benchmark(); printf("sha256_benchmark complete.\n"); printf("Starting sha256_wiki_benchmark.\n"); sha256_wiki_benchmark(); printf("sha256_wiki_benchmark complete.\n"); printf("Starting sha256_arm_benchmark.\n"); sha256_arm_benchmark(); printf("sha256_arm_benchmark complete.\n"); return (0); }