0

I have a weird issue. I'm trying to keccak256 packed encoded arguments in Rust [which should mimic Soldity's behavior to be checked on chain later]. I've failed for now. To figure out the issue I've been writing a small test contract with the following functions:

    function doKeccak(bytes memory _data) public pure returns (bytes32) {
        return keccak256(_data);
    }

    function packed(
        address _from,
        address _to,
        uint256 _value,
        bytes memory _data,
        uint256 _nonce,
        uint256 _gasPrice,
        uint256 _gasLimit
    ) public pure returns (bytes memory) {
        return
            abi.encodePacked(
                bytes1(0x19),
                bytes1(0),
                _from,
                _to,
                _value,
                _data,
                _nonce,
                _gasPrice,
                _gasLimit
            );
    }

   function doKeccakPacked(
        address _from,
        address _to,
        uint256 _value,
        bytes memory _data,
        uint256 _nonce,
        uint256 _gasPrice,
        uint256 _gasLimit
    ) public pure returns (bytes32) {
        return
            keccak256(
                abi.encodePacked(
                    bytes1(0x19),
                    bytes1(0),
                    _from,
                    _to,
                    _value,
                    _data,
                    _nonce,
                    _gasPrice,
                    _gasLimit
                )
            );
    }

Now, from the rust program if I call [pseudo-code] contract.doKeccakPacked(args..) and contract.doKeccak(contract.packed(args)) I get different results ...

Any idea ?

Edit Here is, for instance, a raw Rust params array, which leads to different results:

[
    Address(
        0xdfa1468d07fc86840a6eb53e0e65cebde81d1af9,
    ),
    Address(
        0x7f114fc87edc5f1311d4e77d1a3018a3dc34f47e,
    ),
    Uint(
        0,
    ),
    Bytes(
        [
            201,
            13,
            180,
            71,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            127,
            17,
            79,
            200,
            126,
            220,
            95,
            19,
            17,
            212,
            231,
            125,
            26,
            48,
            24,
            163,
            220,
            52,
            244,
            126,
        ],
    ),
    Uint(
        94481061581245121,
    ),
    Uint(
        0,
    ),
    Uint(
        250000,
    ),
]

Edit 2

Here is the rust code:

fn hash_sign(
        &self,
        wallet: Address,
        data: ethabi::Bytes,
        nonce: U256,
    ) -> Result<Bytes, String> {
        let options = Options::default();

        // this is the contract whose test functions are above
        let erc_1077_hash_abi = ethabi::Contract::load(constants::abis::ERC1077_HASH).unwrap();
        let erc_1077_hash_address = Address::from_str(&"83649266Ba0a8CAd860403a6532F12c6074BBDAC").unwrap();

        let function = erc_1077_hash_abi.function("doKeccakPacked").unwrap();
        let params: [ethabi::Token; 7] = [
            ethabi::Token::Address(self.address),
            ethabi::Token::Address(wallet),
            ethabi::Token::Uint(self.value()),
            ethabi::Token::Bytes(data.clone()),
            ethabi::Token::Uint(nonce),
            ethabi::Token::Uint(self.gas_price()),
            ethabi::Token::Uint(self.gas_limit()),
        ];

        let encoded = function.encode_input(&params).unwrap();
        let result_1 = self.web3.eth().call(
            CallRequest {
                from: None,
                to: erc_1077_hash_address,
                gas: options.gas,
                gas_price: options.gas_price,
                value: options.value,
                data: Some(Bytes(encoded)),
            },
            None.into(),
        );

        let result_1 = match result_1.wait() {
          Ok(s) => Ok(s),
          Err(_e) => Err(format!("error")),
      };

        let function = erc_1077_hash_abi.function("packed").unwrap();
        let encoded = function.encode_input(&params).unwrap();

        let packed = self.web3.eth().call(
            CallRequest {
                from: None,
                to: erc_1077_hash_address,
                gas: options.gas,
                gas_price: options.gas_price,
                value: options.value,
                data: Some(Bytes(encoded.clone())),
            },
            None.into(),
        );

        let packed = packed.wait().unwrap();
        let packed = packed.0;

        let function = erc_1077_hash_abi.function("doKeccak").unwrap();
        let params: [ethabi::Token; 1] = [
            ethabi::Token::Bytes(packed),
        ];
        let encoded = function.encode_input(&params).unwrap();

        let result_2 = self.web3.eth().call(
            CallRequest {
                from: None,
                to: erc_1077_hash_address,
                gas: options.gas,
                gas_price: options.gas_price,
                value: options.value,
                data: Some(Bytes(encoded)),
            },
            None.into(),
        );

        let result_2 = match result_2.wait() {
          Ok(s) => Ok(s),
          Err(_e) => Err(format!("error")),
      };


        result_1
    }

Here result_1 and result_2 are differents ...

10
  • x19 does not compile. Feb 9 '20 at 10:32
  • packed is not a function. Feb 9 '20 at 10:33
  • Other than that (i.e., after fixing these trivial errors), I tested your code and it returns the exact same values. Please provide an example set of arguments which gives a different result. Feb 9 '20 at 10:35
  • Hi, yeah sorry for the typo. I've tested it from Rust [not js] and results do not match. You can set to and from to whatever - different - addresses you want, gasPrice to 0, gasLimit to 250000, nonce to 35681459843, value to 0 and data to whatever bytes array. Thanks for helping! Feb 9 '20 at 10:42
  • I didn't say how I tested it, and again - please provide an example set of arguments which gives a different result for each method. Feb 9 '20 at 10:43
0

Actually I figured it out. It was not a keccak issue but a problem related to the way the web3 rust crate decode bytes data returned from the chain. It enforces some weird padding - turning it into a FixedBytes type - thus messing up with the whole workflow.

Thanks everyone.

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.