bytes32 in functions not working properly (but in use in contracts)

thothrisingthothrising Member Posts: 9
I posted this initially on reddit as I've noticed devs are usually really quick to respond to technical things there, but this is probably a more appropriate place for this. Basically bytes32 function parameters are not working although contracts are using them and I'm wondering where this issue is on the list of Solidity stuff? Below is the full post I put on reddit:

--------------------------------------
The wiki says that bytes in function parameters don't work yet: https://github.com/ethereum/wiki/wiki/Solidity-Features#byte-arrays

But contracts are using bytes in function parameters, such as this gambling contract which uses bytes32: https://explorer.etherapps.info/dice

But exploring using bytes32 as function parameters I get bizarre behavior. Let's do var testingHash = web3.sha3( 'testing' ) to generate a bytes32

then testingHash = 0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02

If we create a contract with the function:

function echo( bytes32 hash ) constant returns (bytes32) { return hash; }

Deploy it and call it with testingHash, we don't get back our original bytes32 object, but a different one: 0x5f16f4c7f149abe752e3d44668a7bd949eb0a533583216b04000000000000000

However if we create the hash from the string with a function in the contract, it works right:

function doSha3( string testingString ) constant returns (bytes32) { return sha3( testingString ); }

calling this with 'testing' gives us what we got from web3.sha3( 'testing'): 0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02

The gambling contract above generates a hash, you send it as a function argument, and then later it compares it... but from what I'm seeing it wouldn't be comparing the actual hash, but something else. This might not actually matter for gambling where the goal of the secret and hash is to provide randomness for later comparison, but it really really matters if you are doing something that requires storing the actual bytes32 hash and then seeing if the 'secret' they used to generate the hash matches later:

if (sha3( secret) == storedBytes32Hash) { //do stuff }

So are bytes still not working properly as function parameters like the wiki states? Should gambling contracts be using them? I'm working on contracts that require storing a hash and then later someone showing the secret that generated it, but currently that doesn't work because the contract stores the original bytes32 hash as something very different. There has been talk about this here, but no one has mentioned running into this problem. Am I just missing something?

Oddly enough passing bytes32 as parameters all within a contract seems to work, it is only when passed as arguments by calling a function from outside (from an account) where it goes wrong.

Anyone else run into this or can shed some light on what is going on?

Comments

  • sillytunasillytuna Member Posts: 38 ✭✭
    Just tried to replicate your problem and couldn't, although did find something concerning.

    To get the test function to work, I had to do "0x" + testingHash so that web3 understood it was hex. That returned back fine when input to a bytes32 function.

    However, this raised an issue. If you pass strings containing only 0-9 or 0x[0-0a-f] into a function expecting a bytes32, it converts it to a number. This can be a problem because if you're using arbitrary 32 byte limited strings, as is very common in reference code for use in mapping objects, then you may get a nasty surprise if your string looks like decimal or hex.
  • SouptacularSouptacular Member, Administrator, Moderator Posts: 14 admin
    I am having the exact same issue. Will try to intelligently elaborate on the issue later after I research it more.
  • thothrisingthothrising Member Posts: 9
    sillytuna,

    Thanks for trying to replicate it. I was sending the bytes32 with the '0x' prefix. With a contract such as:

    contract echoContract {
    function echo( bytes32 hash ) constant returns (bytes32) {
    return hash;
    }
    }

    sent and saved locally as var = echocontract

    And then invoking the function with:

    echocontract.echo( 0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02 )

    I get back:

    0x5f16f4c7f149abe752e3d44668a7bd949eb0a533583216b04000000000000000

    But you are saying you get back:

    0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02

    Right? If so, maybe it's local to my machine or geth version? Does that mean some miners out there might interpret/save bytes32 object correctly and some incorrectly? That would be bad.
  • thothrisingthothrising Member Posts: 9
    Souptacular,

    Thanks, at least I know it isn't just me then. Any idea if this is an EVM thing or just a Solidity thing? Could we get around it currently by using Serpent?

    Exploring more I can easily find multiple bytes32s that echo/get saved in a contract as the same bytes32 object, such as:

    0x0000000000000000000000000000000000000000000000000000000000000001
    and
    0x0000000000000000000000000000000000000000000000000000000000000010

    both echo or get saved as:

    0x1000000000000000000000000000000000000000000000000000000000000000

    and I seem to so far always get many zeros at the end of what echos back... which means I might be wrong about this not being an issue for gambling contracts... it could mean they end up being easily gamed at this point.

    Again I figured this might be known since the wiki states that bytes don't work right currently in Solidity but then I saw them being used in contracts.
  • sillytunasillytuna Member Posts: 38 ✭✭
    If it makes any difference, I build using embark and am running on a local Debian node (private chain).
  • SouptacularSouptacular Member, Administrator, Moderator Posts: 14 admin
    edited August 2015
    I still haven't figured out my issue entirely, but I think it has to do with the Javascript crypto library I am using to hash my string into a SHA3 hash (https://code.google.com/p/crypto-js/). The SHA3 function creates the hash a s a "word array" and not a string. It seems that even when I convert it out of a word array and try to pass it into my function that takes bytes32 it still does not recognize as an appropriate input. I get the error "Uncaught invalid address" which references this code in formatters.js (part of Ethereum JS code):
    var inputAddressFormatter = function (address) {
        var iban = new Iban(address);
        if (iban.isValid() && iban.isDirect()) {
            return '0x' + iban.address();
        } else if (utils.isStrictAddress(address)) {
            return address;
        } else if (utils.isAddress(address)) {
            return '0x' + address;
        }
        throw 'invalid address';
    };
    The closest thing I could find to a code sample that is similar to what I'm trying to do is Notareth, but I have yet to read through all the code yet on it.

    tl;dr: Is it currently possible to pass a SHA3 hash string in Javascript to an Ethereum contract function that takes bytes32 as input? Do I need to rehash the hash into a different format, like HEX, in order for it to work?
  • thothrisingthothrising Member Posts: 9
    Souptacular,

    When you use the javascript crypto library to generate a sha3 hash it does return a string of the hash, for example:
    '0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02'

    I can confirm that if you try to pass that string as an argument into a contract function that has a bytes32 parameter you get an error, e.g. doing this:

    echocontract.echo( '0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02' )

    gives an error when the function echo is:

    function echo( bytes32 hash) constant returns (bytes32) {return hash;}

    But if you pass in what should be a bytes32 object it does echo back a bytes32 object, but not the one I send:

    echocontract.echo( 0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02 )

    should return: 0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02
    but I get: 0x5f16f4c7f149abe752e3d44668a7bd949eb0a533583216b04000000000000000

    Similarly any bytes32 object passed in echoes some other bytes32 object back out. Also if you try to store it, the same thing happens, the contract stores a different bytes32 object, e.g. with a contract like:

    contract testContract{

    bytes32 public hash;

    function saveHash( bytes32 h) { hash = h;}
    }

    if you call saveHash with one bytes32 object, like:

    testContract.saveHash.sendTransaction(0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02,{from: eth.accounts[0]})

    then check what was saved:

    testContract.hash()

    it returns not the bytes32 you sent, but a different bytes32 object, again this one:

    0x5f16f4c7f149abe752e3d44668a7bd949eb0a533583216b04000000000000000

    This is not isolated to this one bytes32 hash, but to any bytes32 sent. Some other object gets saved/processed.

    The problem I am running into is not on the sha3, it is related to using bytes32 as a parameter in a solidity function. I am just using sha3 to generate a bytes32 object, but that isn't important. If you manually pass in a bytes32 object, such as 0x0000000000000000000000000000000000000000000000000000000000000000 into either the echo function or saveHash functions I've mentioned, the bytes32 object that gets saved or echoed back is not what you sent.

    Where sha3 could come into play is if you are later trying to match a secret to a saved bytes32 hash. If someone saves a sha3 hash (a bytes32 object) and later sends a secret, and the contract hashes it with sha3 and tries to compare it, it should match but in the current state it would not (because the original sent hash wouldn't be what was saved).
  • thothrisingthothrising Member Posts: 9
    edited August 2015
    Thinking about this more, I guess the problem could be in the geth instance I am using... if it is mangling the bytes32 object I am trying to send before it gets to the contract (because I can confirm that if you generate a bytes32 object in a contract and pass it around, it works just fine).

    I'll test some more but I may need to raise this issue in a web3 or geth section rather than in the solidity section.
    Post edited by thothrising on
  • thothrisingthothrising Member Posts: 9
    edited August 2015
    Figured out my problem. I'm not sure why the Solidity wiki still states bytes parameters aren't working yet, because seems like all is well (that part of the wiki is probably just out of date).

    The issue was with web3 not being able to handle large numbers (JavaScript). It uses the BigNumber library to work around these issues. This is probably why lots of trailing zeros were always a part of whatever bytes32 was saved or echoed back.

    Anyway, calling an echo function like this succeeds, but doesn't work (as mentioned above):
    echocontract.echo( 0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02 )

    But if you run the bytes32 as a string through the included BigNumber library it works:
    echocontract.echo( web3.toBigNumber( '0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02') )

    as this echoes back:

    '0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02'

    So just need to be careful on the geth side.
  • thothrisingthothrising Member Posts: 9
    It sounds like sillytuna was able to send the hex directly without using web3.toBigNumber. That seems at odds with what I had to do though.
  • sillytunasillytuna Member Posts: 38 ✭✭
    edited August 2015
    Nope, it needs to be through big number if not in quotes. I didn't twig you weren't using it as a string altho stupidly my colleague did say it looks like you hit a float or double limit. He was right!

    Still, I did find that if you put a number in the quotes without letters then it's converted differently from a string, i.e. within web3 there appears to be no way to send a byte32 string which is all numbers - this could be a problem e.g. if people decide to use account numbers as a byte32 key for use in mappings. Maybe using one of the other call functions would avoid the issue.
  • SouptacularSouptacular Member, Administrator, Moderator Posts: 14 admin
    I figured out my issue. It actually had nothing to do with the bytes32, I was getting the invalid address error because my function required the following when calling it:

    Below is what I had written previously that caused the invalid address error:
    var success= storeAFile.newFile(sha3Hash);

    This is what fixed it:
    var success= storeAFile.newFile(sha3Hash, {from: web3.eth.coinbase, gas: 2000000});

    So overall a silly mistake :)
    Always look for the simple things first everyone.
  • fivedogitfivedogit Member Posts: 21
    edited September 2015
    FWIW, I'm encountering a similar issue. I'm using Ping and Pong contracts found here: https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts

    The goal is to set a value on Pong and retrieve it transactionally from Ping.

    // 1. Deploy Pong with a pongval.
    // 2. Deploy Ping, giving it the address of Pong.
    // 3. Call Ping.getPongvalRemote() using a sendTransaction...
    // 4. ... which retreives the value of pongval from Pong.
    // 5. If successful Ping.getPongval() should return the value from step 1.

    Every time I tried this, Ping.getPongval() constant would return "0" when it should have been returning whatever I set the pongval to in Step 1.

    I added a bunch of extra investigative functions to find out what was going on and ran into this:
    
    (... snip ... contracts deployed with Pong(-12) ..) 
    > pong.getPongvalConstant();
    -12     // so far so good
    > ping.getPongvalConstant();  
    -1      // still good. I haven't retrieved it yet.
    > pong.getPongvalTxRetrievalAttempted(); 
    0      // still good. Haven't attempted yet.
    > pong.getAddress();             
    "0x144f881324301f0c8356ae33c9aa223c3facea1e"     // pong knows what its address is. Good.
    > ping.getPongAddress();      
    "0x144f881324301f201ade6b087cfaf59580000000"    // WRONG. Ping has incorrect Pong address.
    > ping.setPongAddress.sendTransaction(0x144f881324301f0c8356ae33c9aa223c3facea1e, {from:eth.coinbase,gas:1000000}); // use setter to force Ping to know Pong's correct address
    "0xb81315cf603a2f2a8675f3c877a68177e7a6daa68ace687b14972420739a9a47"
    > ping.getPongAddress(); 
    "0x144f881324301f201ade6b087cfaf59580000000"  // WRONG. Ping still doesn't know Pong addr.
    
    So what's the issue? geth can't handle address types or something? the "address" type is synonymous with bytes20, if I'm reading correctly. Any ideas?

    EDIT: OK. I got it. When deploying Ping with Pong's address instead of...

    var _pongAddress = 0xe1261b9996c5582a4352065aa269d40e74a91927 ;

    ...I used...

    var _pongAddress = web3.toBigNumber( '0xe1261b9996c5582a4352065aa269d40e74a91927') ;

    and it worked. I should read first next time... Thanks!
Sign In or Register to comment.