1
progma solidity ^0.4.15;
contract Callee 
{
    uint public result_;

    function foo (uint _length, uint[] _nums) public returns (uint)
    {    
        result_ = 0;
        for ( uint i = 0; i < _length; ++i)
             result_ += _nums[i];
        return result_;
    }
}

contract Caller
{
    uint public result_;
    address public callee_ = <Address of deployed Callee>;

    function Call (uint[] _nums) public returns (bool)
    {    return callee_.delegatecall(bytes4(sha3("foo(uint256,uint256[])")), _nums.length, _nums); }
}

These are contracts I have defined. Deploy Callee first, then give its address to Caller contract, then invoke the Call function with parameter [1], however the result_ value is 256 after calling. I tested several data, and find out that each data in the uint256[] param is left-shifted by 8 bits. But param 1, which has type uint256, is not shifted.

I want to know why.

1 Answer 1

1

This is really interesting. It looks like the Solidity compiler is not correctly marshalling the arguments for the delegate call. In particular, nums_ is a dynamic type, but it is not being treated as such.

Let's say that your input is [42]. The call data produced by your code as it stands is

2fbe983a                                                         // Function selector
0000000000000000000000000000000000000000000000000000000000000001 // _nums.length
000000000000000000000000000000000000000000000000000000000000002a // _nums

This is actually incorrect as _nums is a dynamic datatype as per the ABI, and foo() expects it to be encoded as such - when foo decodes this it will get junk: this case actually fails to run at all. In your case, the input [1] is interpreted to mean that the _nums array is at position 0x01 in the calldata, so its length is interpreted as 0x100 and its contents as 0x100 followed by 255 zeros. Hence your result.

The calldata ought to look like this:

2fbe983a                                                         // Function selector
0000000000000000000000000000000000000000000000000000000000000001 // _nums.length
0000000000000000000000000000000000000000000000000000000000000040 // Pointer to _nums array
0000000000000000000000000000000000000000000000000000000000000001 // Length of _nums array (_nums.length)
000000000000000000000000000000000000000000000000000000000000002a // _nums array contents

You can work around this in your code as follows:

return callee_.delegatecall(bytes4(sha3("foo(uint256,uint256[])")), _nums.length, 0x40, _nums.length, _nums);

This inserts the missing structure for the dynamic data. Now you can call it with, say, [1,2,3,4,5] and get the answer 15 (tested).

2
  • You're welcome. When I get a moment I'll look into reporting this as an issue in Solidity (and checking to see if it really is a bug, or expected behaviour). When you are satisfied with the answer it's good practice to mark it "accepted" for the benefit of others. Commented Aug 31, 2017 at 5:53
  • It turns out that this is already raised as an issue on the Solidity github. It's been classified as a "documentation issue", and my proposed workaround is the correct way to go. Commented Aug 31, 2017 at 20:36

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.