Vyper: Malformed data when passing a tuple or struct that contains an array

Created on 6 Feb 2020  路  14Comments  路  Source: vyperlang/vyper

Version Information

  • vyper Version (output of vyper --version): 0.1.0-beta16 (current master)
  • OS: linux
  • Python Version: 3.7

What's your issue about?

As reported by @michwill on Gitter, passing a struct between functions, where the struct contains an array, results in malformed data. WIth the following contract:

struct A:
    many: uint256[4]
    one: uint256

@private
@constant
def _foo(_many: uint256[4], _one: uint256) -> A:
    return A({many: _many, one: _one})

@public
@constant
def foo() -> A:
    return self._foo([1, 2, 3, 4], 5)

@public
@constant
def bar() -> (uint256[4], uint256):
    out: A = self._foo([1, 2, 3, 4], 5)
    return out.many, out.one

Here is the output of each public function:

>>> contract.foo()
((1, 2, 5, 0), 0)
>>> contract.bar()
((0, 0, 5, 4), 3)

Observations:

  1. Generating the struct within the public function and returning it, or a component of it, works as expected.
  2. Passing the struct between public functions of two different contracts works as expected.
  3. Structs that do not contain arrays work as expected.
  4. Removing the @constant modifier has no effect.

How can it be fixed?

It looks like an off-by-one (or 2?) error to me, but I'm unsure how to fix it.

bug

Most helpful comment

So - I could fix the unpacking code in self_call.py but I think what I want to do is change our internal calling convention to use unpacked data instead of ABI-encoded data. It will simplify the compiler and reduce code size.

All 14 comments

Do you get the same answers if you reverse the calling order?

The answers seems different if the order is changed in the declaration of struct

I get differently incorrect answers. Placing the non-array before the array does NOT result in the non-array being correctly located so I think the issue is with the struct itself, not just how the array is handled inside the struct... if that makes sense.

Yes, that's exactly what I am observing, too

Any hints of why could that be happening?

Been looking at this some more. The issue isn't only with structs, it also happens when passing a tuple that contains an array.

Looking at the IR it seems that there's an extra MSTORE which overwrites one of the return values. The section of the code where the instruction is generated is: vyper/parser/self_call.py#L227

I think this is a lingering issue from #1838 - @charles-cooper any chance you could take a look?

Good call, seems like we can't draft a new release until we figure out this issue

@iamdefinitelyahuman thanks - I will take a look later today or tomorrow.

Any luck?

hey @michwill - looking into this now

probably declared victory on https://github.com/vyperlang/vyper/issues/1839 and #1409 a little too soon

Been looking at this some more. The issue isn't only with structs, it also happens when passing a tuple that contains an array.

Looking at the IR it seems that there's an extra MSTORE which overwrites one of the return values. The section of the code where the instruction is generated is: vyper/parser/self_call.py#L227

I think this is a lingering issue from #1838 - @charles-cooper any chance you could take a look?

@iamdefinitelyahuman which extra mstore do you see?

i see the issue - call-to-self code assumes that tuple/struct members will always be 1 word

https://github.com/vyperlang/vyper/blob/6ea96f0903e5a6a7f3eb418c3126d3a4d966d395/vyper/parser/self_call.py#L242

So - I could fix the unpacking code in self_call.py but I think what I want to do is change our internal calling convention to use unpacked data instead of ABI-encoded data. It will simplify the compiler and reduce code size.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nrryuya picture nrryuya  路  4Comments

fubuloubu picture fubuloubu  路  3Comments

jacqueswww picture jacqueswww  路  4Comments

ben-kaufman picture ben-kaufman  路  4Comments

haydenadams picture haydenadams  路  3Comments