-
Notifications
You must be signed in to change notification settings - Fork 4
Vyper compiler bug for functions returning byte arrays
- vyper Version (output of
vyper --version
): 0.1.0b10
A function whose return type is bytes[n]
where n < 16
, returns a value that does not conform to the ABI encoding, having incorrect zero-padding.
For example, the get_deposit_count
function of the deposit contract, whose return type is bytes[8]
, returns the following 96 bytes (in hexadecimal notation):
0x0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000008
________________000000000000000000000000000000000000000000000020
^^ <-- problem!
where the first 32 bytes (the first line) denotes the header (the offset 32 = 0x20), the second 32 bytes (the second line) denotes the length of the byte array (8), and the "________________
" denotes the content of the byte array (bytes[8]
). Here, the problem is that the last byte is 32 (= 0x20) while it should be 0 (= 0x00) according to the ABI specification. This means that any contract (written in either Solidity or Vyper) that calls to this type of functions expecting the correct zero-padding may misbehave.
What happens is that the compiled bytecode of get_deposit_count
fails to sufficiently pad zeros when it prepares for the return value. In the following return statement,
return self.to_little_endian_64(self.deposit_count)
it first copies the returned value (of 8 bytes) to some specific region of the memory, and puts only 8 bytes of zero-padding after that, instead of 24 bytes of zeros, which results in including some garbage values in the last 16 bytes. Indeed, in this particular case, the last 16 bytes (000000000000000000000000020
) came from the side-effect of the sub-function call to self.to_little_endian_64()
.
To be more specific, in the following zero-padding LLL code of get_deposit_count
(generated by https://github.com/ethereum/vyper/blob/cdfb36d0c6d4a38ee3d9d34e001b141ec7c6f55f/vyper/parser/stmt.py#L770-L789):
/* Zero pad */
[with,
_ceil32_end,
[ceil32, [mload, 640]],
[repeat,
736,
[mload, 640],
??? --> 8,
[seq,
[if, [gt, [mload, 736], _ceil32_end], break],
[mstore8, [add, 672, [mload, 736]], 0]]]],
[mstore, 608, 32],
[return, 608, [ceil32, [add, [mload, 640], 64]]],
# Line 58
stop]],
the third argument of the repeat
loop is 8, where it should be at least 24. In a quick examination of the Vyper compiler code, it seems that the number 8
comes from the maxlen
of the type bytes[8]
, but I couldn't have a chance to further investigate the root cause and a quick fix.
Indeed, the same problem happens in the to_little_endian_64
function as well.