From dca951b43fc1786a2af51ad51a7aeb06e9d6d0eb Mon Sep 17 00:00:00 2001 From: Ser Doggo <3859395+fubuloubu@users.noreply.github.com> Date: Sat, 23 Jan 2021 16:13:07 -0500 Subject: [PATCH 1/5] refactor: change forwarder proxy bytecode to ERC-1167 --- vyper/functions/functions.py | 46 ++++++++++++------------------------ 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/vyper/functions/functions.py b/vyper/functions/functions.py index 22528e0ac0..56ca06a91f 100644 --- a/vyper/functions/functions.py +++ b/vyper/functions/functions.py @@ -1426,51 +1426,35 @@ def build_LLL(self, expr, context): def get_create_forwarder_to_bytecode(): - from vyper.compile_lll import assembly_to_evm, num_to_bytearray + from vyper.compile_lll import assembly_to_evm code_a = [ - "PUSH1", - 0x33, - "PUSH1", - 0x0C, - "PUSH1", - 0x00, - "CODECOPY", - "PUSH1", - 0x33, - "PUSH1", - 0x00, - "RETURN", "CALLDATASIZE", - "PUSH1", - 0x00, - "PUSH1", - 0x00, + "RETURNDATASIZE", + "RETURNDATASIZE", "CALLDATACOPY", - "PUSH2", - num_to_bytearray(0x1000), - "PUSH1", - 0x00, + "RETURNDATASIZE", + "RETURNDATASIZE", + "RETURNDATASIZE", "CALLDATASIZE", - "PUSH1", - 0x00, + "RETURNDATASIZE", "PUSH20", # [address to delegate to] ] code_b = [ "GAS", "DELEGATECALL", + "RETURNDATASIZE", + "DUP3", + "DUP1", + "RETURNDATACOPY", + "SWAP1", + "RETURNDATASIZE", + "SWAP2", "PUSH1", - 0x2C, # jumpdest of whole program. + 0x2B, # jumpdest of whole program. "JUMPI", - "PUSH1", - 0x0, - "DUP1", "REVERT", "JUMPDEST", - "PUSH2", - num_to_bytearray(0x1000), - "PUSH1", - 0x00, "RETURN", ] return assembly_to_evm(code_a)[0] + (b"\x00" * 20) + assembly_to_evm(code_b)[0] From 8c276fbea9f11610af1078cc1c8676e48b207c0f Mon Sep 17 00:00:00 2001 From: Ser Doggo <3859395+fubuloubu@users.noreply.github.com> Date: Sat, 23 Jan 2021 16:46:34 -0500 Subject: [PATCH 2/5] refactor: make code a little clearer --- vyper/functions/functions.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/vyper/functions/functions.py b/vyper/functions/functions.py index 56ca06a91f..b78091c0fb 100644 --- a/vyper/functions/functions.py +++ b/vyper/functions/functions.py @@ -1428,7 +1428,7 @@ def build_LLL(self, expr, context): def get_create_forwarder_to_bytecode(): from vyper.compile_lll import assembly_to_evm - code_a = [ + forwarder_pre_asm = [ "CALLDATASIZE", "RETURNDATASIZE", "RETURNDATASIZE", @@ -1440,7 +1440,7 @@ def get_create_forwarder_to_bytecode(): "RETURNDATASIZE", "PUSH20", # [address to delegate to] ] - code_b = [ + forwarder_post_asm = [ "GAS", "DELEGATECALL", "RETURNDATASIZE", @@ -1457,7 +1457,7 @@ def get_create_forwarder_to_bytecode(): "JUMPDEST", "RETURN", ] - return assembly_to_evm(code_a)[0] + (b"\x00" * 20) + assembly_to_evm(code_b)[0] + return assembly_to_evm(forwarder_pre_asm)[0], assembly_to_evm(forwarder_post_asm)[0] class CreateForwarderTo(_SimpleBuiltinFunction): @@ -1476,16 +1476,14 @@ def build_LLL(self, expr, args, kwargs, context): ) placeholder = context.new_internal_variable(ByteArrayType(96)) - kode = get_create_forwarder_to_bytecode() - high = bytes_to_int(kode[:32]) - low = bytes_to_int((kode + b"\x00" * 32)[47:79]) + forwarder_pre_evm, forwarder_post_evm = get_create_forwarder_to_bytecode() return LLLnode.from_list( [ "seq", - ["mstore", placeholder, high], + ["mstore", placeholder, bytes_to_int(forwarder_pre_evm)], ["mstore", ["add", placeholder, 27], ["mul", args[0], 2 ** 96]], - ["mstore", ["add", placeholder, 47], low], + ["mstore", ["add", placeholder, 47], bytes_to_int(forwarder_post_evm)], ["clamp_nonzero", ["create", value, placeholder, 96]], ], typ=BaseType("address"), From 4e21dfe77453530f4be5735c43eec8e8d1fe7a4b Mon Sep 17 00:00:00 2001 From: Just some guy <3859395+fubuloubu@users.noreply.github.com> Date: Sat, 23 Jan 2021 18:59:05 -0500 Subject: [PATCH 3/5] refactor: optimized loader bytecode a bit --- vyper/functions/functions.py | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/vyper/functions/functions.py b/vyper/functions/functions.py index b78091c0fb..735c2e1301 100644 --- a/vyper/functions/functions.py +++ b/vyper/functions/functions.py @@ -1428,6 +1428,18 @@ def build_LLL(self, expr, context): def get_create_forwarder_to_bytecode(): from vyper.compile_lll import assembly_to_evm + loader_asm = [ + "RETURNDATASIZE", + "PUSH1", + 0x2D, + "DUP1", + "PUSH1", + 0x0A, + "RETURNDATASIZE", + "CODECOPY", + "DUP2", + "RETURN", + ] forwarder_pre_asm = [ "CALLDATASIZE", "RETURNDATASIZE", @@ -1457,7 +1469,11 @@ def get_create_forwarder_to_bytecode(): "JUMPDEST", "RETURN", ] - return assembly_to_evm(forwarder_pre_asm)[0], assembly_to_evm(forwarder_post_asm)[0] + return ( + assembly_to_evm(loader_asm)[0], + assembly_to_evm(forwarder_pre_asm)[0], + assembly_to_evm(forwarder_post_asm)[0], + ) class CreateForwarderTo(_SimpleBuiltinFunction): @@ -1476,15 +1492,22 @@ def build_LLL(self, expr, args, kwargs, context): ) placeholder = context.new_internal_variable(ByteArrayType(96)) - forwarder_pre_evm, forwarder_post_evm = get_create_forwarder_to_bytecode() + loader_evm, forwarder_pre_evm, forwarder_post_evm = get_create_forwarder_to_bytecode() + # Adjust to 32-byte boundaries + preamble_length = len(loader_evm) + len(forwarder_pre_evm) + forwarder_preamble = bytes_to_int( + loader_evm + forwarder_pre_evm + b"\x00" * (32 - preamble_length) + ) + forwarder_post = bytes_to_int(forwarder_post_evm + b"\x00" * (32 - len(forwarder_post_evm))) + target_address = args[0] return LLLnode.from_list( [ "seq", - ["mstore", placeholder, bytes_to_int(forwarder_pre_evm)], - ["mstore", ["add", placeholder, 27], ["mul", args[0], 2 ** 96]], - ["mstore", ["add", placeholder, 47], bytes_to_int(forwarder_post_evm)], - ["clamp_nonzero", ["create", value, placeholder, 96]], + ["mstore", placeholder, forwarder_preamble], + ["mstore", ["add", placeholder, preamble_length], target_address], + ["mstore", ["add", placeholder, preamble_length + 20], forwarder_post], + ["create", value, placeholder, preamble_length + 20 + len(forwarder_post_evm)], ], typ=BaseType("address"), pos=getpos(expr), From 79195749ec8a8fbe7989a29225973427d20cb61c Mon Sep 17 00:00:00 2001 From: Ben Hauser Date: Tue, 26 Jan 2021 23:14:37 +0100 Subject: [PATCH 4/5] fix: simplify loader bytecode, shift target address --- vyper/functions/functions.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/vyper/functions/functions.py b/vyper/functions/functions.py index 735c2e1301..7bcc379906 100644 --- a/vyper/functions/functions.py +++ b/vyper/functions/functions.py @@ -1429,15 +1429,14 @@ def get_create_forwarder_to_bytecode(): from vyper.compile_lll import assembly_to_evm loader_asm = [ - "RETURNDATASIZE", "PUSH1", 0x2D, - "DUP1", + "RETURNDATASIZE", + "DUP2", "PUSH1", - 0x0A, + 0x09, "RETURNDATASIZE", "CODECOPY", - "DUP2", "RETURN", ] forwarder_pre_asm = [ @@ -1499,7 +1498,13 @@ def build_LLL(self, expr, args, kwargs, context): loader_evm + forwarder_pre_evm + b"\x00" * (32 - preamble_length) ) forwarder_post = bytes_to_int(forwarder_post_evm + b"\x00" * (32 - len(forwarder_post_evm))) - target_address = args[0] + + if args[0].typ.is_literal: + target_address = args[0].value * 2 ** 96 + elif version_check(begin="constantinople"): + target_address = ["shl", 96, args[0]] + else: + target_address = ["mul", args[0], 2 ** 96] return LLLnode.from_list( [ From b1bf2148412ef08cb59d09cc39550784ea1243b4 Mon Sep 17 00:00:00 2001 From: Just some guy <3859395+fubuloubu@users.noreply.github.com> Date: Fri, 12 Feb 2021 22:35:51 -0500 Subject: [PATCH 5/5] test: fixup forwarder test --- tests/parser/functions/test_raw_call.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/parser/functions/test_raw_call.py b/tests/parser/functions/test_raw_call.py index ffa3c3f33a..b97d51e1c4 100644 --- a/tests/parser/functions/test_raw_call.py +++ b/tests/parser/functions/test_raw_call.py @@ -1,5 +1,6 @@ import pytest +from hexbytes import HexBytes from vyper import compiler from vyper.exceptions import ArgumentException, StateAccessViolation from vyper.functions import get_create_forwarder_to_bytecode @@ -63,15 +64,15 @@ def create_and_return_forwarder(inp: address) -> address: assert c2.create_and_call_returnten(c.address) == 10 c2.create_and_call_returnten(c.address, transact={}) - expected_forwarder_code_mask = get_create_forwarder_to_bytecode()[12:] + _, preamble, callcode = get_create_forwarder_to_bytecode() c3 = c2.create_and_return_forwarder(c.address, call={}) c2.create_and_return_forwarder(c.address, transact={}) c3_contract_code = w3.toBytes(w3.eth.getCode(c3)) - assert c3_contract_code[:14] == expected_forwarder_code_mask[:14] - assert c3_contract_code[35:] == expected_forwarder_code_mask[35:] + assert c3_contract_code[:10] == HexBytes(preamble) + assert c3_contract_code[-15:] == HexBytes(callcode) print("Passed forwarder test") # TODO: This one is special