1

I am having trouble doing some string comparison against the last 2 digits of a block hash in solidity. I'm also a little bit confused about how to work with a single character, as it appears that a single byte in solidity contains 2 characters.

I grab a blockhash:

bytes32 myHash = block.blockhash(block.number - 1);

And I get something like:

"0x18676e992055c057538d59b378271bb4eacdb7f6abf9e815fd63255dc11166b6"

I attempt to grab the last character by doing:

byte x = myHash[31];

This gives me a value like this, when printed in a log:

"b600000000000000000000000000000000000000000000000000000000000000"

This is where I'm confused. This is the last 2 characters of the bytes32. I want to use the following function to determine the hex integer value of the character:

function getCoordinate(byte val) returns(uint) {
    if (val == "0") {
        return 0;
    } else if (val == "1") {
        return 1;
    } else if (val == "2") {
        return 2;
    } else if (val == "3") {
        return 3;
    } else if (val == "4") {
        return 4;
    } else if (val == "5") {
        return 5;
    } else if (val == "6") {
        return 6;
    } else if (val == "7") {
        return 7;
    } else if (val == "8") {
        return 8;
    } else if (val == "9") {
        return 9;
    } else if (val == "a") {
        return 10;
    } else if (val == "b") {
        return 11;
    } else if (val == "c") {
        return 12;
    } else if (val == "d") {
        return 13;
    } else if (val == "e") {
        return 14;
    } else if (val == "f") {
        return 15;
    }
}

However, none of the cases match, so it always returns 0. Anyone have any knowledge of how to get a single character so that this function will work properly?

  • What do you mean by hex integer value? Are you saying you want to convert the last byte in the hash to its integer value? – jojeyh Oct 26 '17 at 3:32
  • Yeah exactly, I worded that poorly. I want to get from the hexadecimal character in the bytes32 to an integer value (between 1 and 16) – John Palmer Oct 26 '17 at 3:37
2

Since every byte is encoded as 2 hexadecimal characters (e.g. in your example the last byte is 0xb6) you need 2 functions: one for the left coordinate, another for the right coordinate:

function getRightCoordinate(byte input) returns(uint) {
    byte val = input & byte(15);
    return uint(val);
}

function getLeftCoordinate(byte input) returns(uint) {
    byte val = input >> 4;
    return uint(val);
}

15 is the decimal representation of the binary 00001111. Bitwise & with it gives you the right coordinate.

>> 4 shifts the first 4 bits to the right discarding the other 4 bits.

  • Thanks, this is exactly what I was looking for, and it's more efficient than the answer I posted. Accepting this answer. – John Palmer Oct 27 '17 at 3:58
1

Bytes have 8 bits, so a single byte will contain up to 256 (2*2*2*2*2*2*2*2) values. A hex character is 16 values (10 digits + 6 letters), so you use 2 hex characters when you represent a byte (16*16 = 256).

bytes32 data types can be seamlessly converted into a number of type uint256 (32 8-bit bytes makes 256 bits), so the obvious thing to do is just turn the block hash into a number, divide it by 16 and take the remainder. The following (untested) code should give you a number from 0 to 15:

uint256 your_num = uint256(block.blockhash(block.number - 1)) % 16;

  • Awesome! Going to try this as soon as I am back at my computer. Will follow up here after. – John Palmer Oct 26 '17 at 6:07
  • I guess I misread this earlier because this won't quite work for me. I need to get an integer 1-16 specifically from the last digit of the blockhash. I can't use all of the characters. The reason is that I will need to do this for another character as well. e.g. Last characters are "0a" then the 2 numbers are 0 and 10. – John Palmer Oct 26 '17 at 15:39
0

I was able to use Edmund's hint of working with uint256 to find a solution to my problem. By some creative bit shifting, you can get the integer value of a single digit. In my case, I wanted the last 2 digits of the blockhash.

bytes32 myHash = block.blockhash(block.number - 1);
uint256 hashNum = uint256(myHash);

// Last Character alone
// Shift left by 252 bits (leaving just 4 bits left), then shift back to the right
uint256 last = (hashNum * 2 ** 252) / (2 ** 252);

// Second to last character alone
// Shift left by 248 bits (leaving 8 bits left), but then shift right by 252
uint256 secondLast = (hashNum * 2 ** 248) / (2 ** 252);

So there you have it, the final 2 characters of the blockhash as integers between 0 - 15.

There is a more optimal solution using bit masking instead of shifting. I will post it here later if I get it working.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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