1

I need to successfully generate a hash value based on what i feel so far has been described as a hexadecimal filtered sha256 implementation... or something like that. I've been here , here , here, here and here ... amongst many other places.


Example Case:

Using "abc" as the input string, AnyHash.com with the 'Hex' checkbox ticked produces a hash - 15f27c6b8828e62cd3d09e8b4de5230844facfc88f1b9c34129cab7706f2f561. And when 15f27c6b8828e62cd3d09e....... is fed as the input it produces the hash - 42a5aa8cd08811f43428c42f76279a5a92e12b3e3f7080b87804a05135773487.

Following the comment by dave_thompson_085 i've been trying to make it work. The sha256 implementation in use is the one provided by Zedwood and below is my unfruitful struggle to recreate the hash output as generated by AnyHash.com.


//  -----------------------                 --------------------------- //
//  -----------------------                 --------------------------- //
//  -----------------------                 --------------------------- //
// COMMENT BY DAVE_THOMPSON_085

// That code is okay except the silly std::string wrapper is useless for bitcoin. Do something like :
// unsigned char hdr[80], hash[32];
// SHA256 ctx = SHA256();

/* fill in hdr */
// ctx.init();
// ctx.update(hdr,80);
// ctx.final(hash);
// ctx.init();
// ctx.update(hash,32);
// ctx.final(hash);
/* (reverse and) use results in hash, possibly converted to hex or whatever you need */

//  -----------------------                 --------------------------- //
//  -----------------------                 --------------------------- //
//  -----------------------                 --------------------------- //

std::string sha256(std::string input)
{
    string kk;
    // header_data is "160" in size because midstate optimization is not implemented
    unsigned char header_data[160], hash[32];
    char hash2[32], hash3[32];
    char buf[2*32+1];
    buf[2*32] = 0;

    // input = GetBinaryStringFromHexString(input); // convert input data to binary format
    init();
    update((unsigned char*)input.c_str(), input.length());
    final(hash);

    // Iterate and store values from hash[] to hash2[]
    for (int i = 0; i < 32; i++)
        hash2[i] = hash[i];

    // kk = final2(hash); // MODIFIED FUNCTION THAT RETURNS A STRING 
    // //kk = char_ToString(hash2); // Converts char array to string variable
    kk = GetBinaryStringFromHexString(char_ToString(hash2)); // convert data to binary format
    input = kk; // Store data in kk to input
    init();
    update((unsigned char*)input.c_str(), input.length());
    final((unsigned char *)hash3); // Modified as char* to unsigned char* conversion is not allowed


    for (int i = 0; i < 32; i++) 
        sprintf(buf+i*2, "%02x", hash3[i]); // ORIGINAL : sprintf(buf+i*2, "%02x", digest[i])

    return std::string(buf); // Return buf as string instead of char
}


int main(int argc, char const *argv[])
{
    std::string data = "abc";
    std::cout << "sha256(data) : " << sha256(data) << "\n";
    std::cout << "sha256(sha256(data)) :  " << sha256(sha256(data)) << "\n";
    return 0;
}

//  Input : abc
//  Expecting first hash output from sha256 ( with hex conversion ) to be :
// 15f27c6b8828e62cd3d09e8b4de5230844facfc88f1b9c34129cab7706f2f561 - single sha

//  And second output to be : 
// 42a5aa8cd08811f43428c42f76279a5a92e12b3e3f7080b87804a05135773487 - double sha

//  As generated byanyhash.com

It's always the obvious implementations that are sometimes the hardest to implement. - in my opinion.

The same hash generator with the 'Hex' ticked successfully generates the correct hash of block 100000 existing in the bitcoin blockchain. Block 100,000 hashed by AnyHash.com

Block header 0100000050120119172a610421a6c3011dd330d9df07b63616c2cc1f1cd00200000000006657a9252aacd5c0b2940996ecff952228c3067cc38d4885efb5a4ac4247e9f337221b4d4c86041b0f2b5710

I tried getting the code from bitcoin source code hahahahahahahahahahahaha....... need i say more?

With so many headers included and dependencies beyond what is needed, just thinking about it was a nightmare. So please, if anyone knows how to actually make this code work... please assist a fellow programmer..

Lord knows all things bitcoin make most programmers look like a novice. A great technology nonetheless!

Below are the other functions used(in the event it has something wrong with it) Thanks in advance!

sing std::hex;

string GetBinaryStringFromHexString(string hexData);
// std::string char_ToString(char data[]);

std::string char_ToString(char data[])
{
    std::string stringData = ""; //declare string variable
    // int size = sizeof(data)/sizeof(*data); 
    int size =  strlen(data); //get amount of characters in char array
    for (int i = 0; i < size; i++) // for elements in char array
        stringData = data; // store elements in string variable "stringData"
    return stringData; // return stringData
}


string GetBinaryStringFromHexString(string hexData)
{
    string binaryResult = "";
    for (int i = 0; i < hexData.length(); ++i)
    {
        switch (hexData[i])
        {
        case '0':
            binaryResult.append("0000");
            break;
        case '1':
            binaryResult.append("0001");
            break;
        case '2':
            binaryResult.append("0010");
            break;
        case '3':
            binaryResult.append("0011");
            break;
        case '4':
            binaryResult.append("0100");
            break;
        case '5':
            binaryResult.append("0101");
            break;
        case '6':
            binaryResult.append("0110");
            break;
        case '7':
            binaryResult.append("0111");
            break;
        case '8':
            binaryResult.append("1000");
            break;
        case '9':
            binaryResult.append("1001");
            break;
        case 'a':
            binaryResult.append("1010");
            break;
        case 'b':
            binaryResult.append("1011");
            break;
        case 'c':
            binaryResult.append("1100");
            break;
        case 'd':
            binaryResult.append("1101");
            break;
        case 'e':
            binaryResult.append("1110");
            break;
        case 'f':
            binaryResult.append("1111");
            break;
        }
    }
    return binaryResult;
}


2 Answers 2

3

Note that I am analyzing your code as if I compiled it as is. That means anything that is commented out is ignored.


The first major issue is this line:

    kk = GetBinaryStringFromHexString(char_ToString(hash2)); // convert data to binary format

The earlier final(hash); will have produced a char array filled with the SHA256 hash as binary data. Thus there is no need to convert that to a std::string and subsequently try to convert that to a binary string. This line is unnecessary and hash could be provided directly as input to the next update(). Fixing this should allow you to find the double SHA256 of abc.

The second major issue with this code is that GetBinaryStringFromHexString is not doing what you think it is doing. It is converting a string of hex to a string of binary. That is not the same as converting a string of hex to the binary data represented by that hex. What you actually end up with in that function is a much longer string whose data is the ascii characters for 0 and 1, not the data represented by the hex string itself. This StackOverflow answer provides a hex string parsing function that you can use. Fixing this should allow you to find to find the double SHA256 of a block header that is provided as a hex string.


A couple of minor issues:

  1. There is no need to duplicate hash to hash2. You can provide it directly as the parameter to the second update too.
  2. There is no need to have hash2 or hash3. You can use hash in final each time and it will be overwritten with the new hash.
  3. The function char_ToString is not necessary. Besides that you don't need it for your code, std::string has a constructor that can take a char * and its length.

Your final code would be something like:

std::string sha256(std::string input)
{
    string kk;
    // header_data is "160" in size because midstate optimization is not implemented
    unsigned char header_data[160], hash[32];
    char buf[2*32+1];
    buf[2*32] = 0;

    input = ParseHex(input);
    init();
    update((unsigned char*)input.c_str(), input.length());
    final(hash);
    
    init();
    update(hash, 32);
    final(hash);

    for (int i = 0; i < 32; i++) 
        sprintf(buf+i*2, "%02x", hash[i]); // ORIGINAL : sprintf(buf+i*2, "%02x", digest[i])

    return std::string(buf); // Return buf as string instead of char
}

std::string ParseHex(const std::string& s)
{
    assert(s.size() % 2 == 0);
    static const std::size_t symbol_count = 256;
    static const unsigned char hex_to_bin[symbol_count] = {
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00 - 0x07
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08 - 0x0F
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10 - 0x17
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x18 - 0x1F
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 - 0x27
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28 - 0x2F
                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x30 - 0x37
                 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x38 - 0x3F
                 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, // 0x40 - 0x47
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x48 - 0x4F
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 - 0x57
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x58 - 0x5F
                 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, // 0x60 - 0x67
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x68 - 0x6F
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x70 - 0x77
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x78 - 0x7F
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80 - 0x87
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x88 - 0x8F
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x90 - 0x97
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x98 - 0x9F
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA0 - 0xA7
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA8 - 0xAF
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xB0 - 0xB7
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xB8 - 0xBF
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC0 - 0xC7
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC8 - 0xCF
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD0 - 0xD7
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD8 - 0xDF
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xE0 - 0xE7
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xE8 - 0xEF
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xF0 - 0xF7
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // 0xF8 - 0xFF
               };

    std::string out;
    auto& itr = s.begin();
    while (itr != s.end())
    {
       unsigned char b = static_cast<unsigned char>(hex_to_bin[*(itr++)] << 4);
       b |= static_cast<unsigned char>(hex_to_bin[*(itr++)]     );
       out.push_back(b);
    }
    return out;
}
9
  • You said .. Does that imply that i need a hex parser?? And if i do, where exactly will that parser be used? What is the simplified pseudo code according to your answer? Commented Jan 12, 2022 at 6:39
  • Also, the unnecessary hash2 and hash3 as well as the functions char_ToString() and GetBinaryStringFromHexString() have been removed! The no. 3 on your list of minor issues clearly states that the ` char_ToString` is not necessary... I'm assuming that your reference to the std::string constructor is merely a reference to a more healthy and accurate approach - in the event that a conversion was actually required . Commented Jan 12, 2022 at 6:52
  • 1
    @AncientTides The hex parser function would be used where you currently have // input = GetBinaryStringFromHexString(input);, once you uncomment that line so you can try to hash the hex for a block header. I will edit with modified code.
    – Ava Chow
    Commented Jan 12, 2022 at 17:44
  • 2
    ParseHex is a placeholder for the hex parsing function from the stack overflow answer I linked. Figuring out how to actually use that function is left as an exercise for the reader.
    – Ava Chow
    Commented Jan 13, 2022 at 0:15
  • 1
    I've added a ParseHex function that might work. I did not compile this code, but logically it should work.
    – Ava Chow
    Commented Jan 13, 2022 at 1:53
1

Alright so it's been quite a ride on this specific task but Thanks to Andrew Chow there's finally a working version that I know a lot of newbies and even intermediate developers & programmers will surely find useful.

Had to make slight modifications to the ParseHex() provided by Andrew Chow....

Headers needed are:

#include <string>
#include <cstring>
#include <assert.h> // assert() is used

Sha256 implementation in use is the one provided by Zedwood !!

First is the ParseHex function provided by Andrew Chow


std::string ParseHex(std::string& s)
{
    assert(s.size() % 2 == 0);
    static const std::size_t symbol_count = 256;
    static const unsigned char hex_to_bin[symbol_count] = 
    {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00 - 0x07
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08 - 0x0F
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10 - 0x17
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x18 - 0x1F
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 - 0x27
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28 - 0x2F
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x30 - 0x37
        0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x38 - 0x3F
        0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, // 0x40 - 0x47
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x48 - 0x4F
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 - 0x57
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x58 - 0x5F
        0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, // 0x60 - 0x67
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x68 - 0x6F
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x70 - 0x77
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x78 - 0x7F
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80 - 0x87
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x88 - 0x8F
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x90 - 0x97
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x98 - 0x9F
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA0 - 0xA7
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA8 - 0xAF
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xB0 - 0xB7
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xB8 - 0xBF
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC0 - 0xC7
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC8 - 0xCF
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD0 - 0xD7
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD8 - 0xDF
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xE0 - 0xE7
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xE8 - 0xEF
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xF0 - 0xF7
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // 0xF8 - 0xFF
    };

    std::string out;
    auto itr = s.begin();
    while (itr != s.end())
    {
       unsigned char b = static_cast<unsigned char>(hex_to_bin[*(itr++)] << 4);
       b |= static_cast<unsigned char>(hex_to_bin[*(itr++)]     );
       out.push_back(b);
    }
    return out;
}

Then next is the modified sha256 function call.


std::string sha256(std::string input)
{
    string kk;
    int size = input.length();
    
    unsigned char header_data[size], hash[32];// header_data is "160" in size because midstate optimization is not implemented
    char buf[2*32+1];
    buf[2*32] = 0;

    input = ParseHex(input); // ParseHex() is UNDEFINED !!!
    init(); // Initiialize
    update((unsigned char*)input.c_str(), input.length()); // Update
    final(hash); // Finalize

    // Uncomment code below if you want to directly double hash input data using a single "sha256(input)" function call
    // init();
    // update(hash, 32);
    // final(hash);

    for (int i = 0; i < 32; i++) 
        sprintf(buf+i*2, "%02x", hash[i]); // feed data from hash into buf and format data

    return std::string(buf); // Return buf as string instead of char
}

  • THESE FUNCTIONS NEED TO BE PASTED INSIDE THE SHA256 HEADER FILE
  • YOU'D HAVE TO REVERSE ENDIANESS OF HASH RESULT.
  • ALSO, THE "assert()" THROWS AN ERROR FOR UNEVEN LENGTHED INPUTS.. SO "abc" WON'T WORK.

Hope this helps you if you ever need this....

Cheers

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.