Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions tests/functional/builtins/codegen/test_create_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,51 @@ def deploy_from_calldata(s: Bytes[1024], arg: uint256, salt: bytes32) -> address
assert env.get_code(res) == runtime


# test that create_from_blueprint bubbles up revert data
def test_bubble_revert_data_blueprint(get_contract, tx_failed, deploy_blueprint_for):
ctor_code = """
@deploy
def __init__():
raise "bad ctor"
"""

f, _ = deploy_blueprint_for(ctor_code)

deployer_code = """
@external
def deploy_from_address(t: address) -> address:
return create_from_blueprint(t)
"""

deployer = get_contract(deployer_code)

with tx_failed(exc_text="bad ctor"):
deployer.deploy_from_address(f.address)


# test that raw_create bubbles up revert data
def test_bubble_revert_data_raw_create(get_contract, tx_failed):
to_deploy_code = """
@deploy
def __init__():
raise "bad ctor"
"""

out = compile_code(to_deploy_code, output_formats=["bytecode"])
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x"))

deployer_code = """
@external
def deploy_from_calldata(s: Bytes[1024]) -> address:
return raw_create(s)
"""

deployer = get_contract(deployer_code)

with tx_failed(exc_text="bad ctor"):
deployer.deploy_from_calldata(initcode)


# test raw_create with all combinations of value and revert_on_failure kwargs
# (including not present at all)
# additionally parametrize whether the constructor reverts or not
Expand Down
8 changes: 4 additions & 4 deletions vyper/builtins/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
bytes_data_ptr,
calculate_type_for_external_return,
check_buffer_overflow_ir,
check_create_operation,
check_external_call,
clamp,
clamp2,
clamp_basetype,
clamp_nonzero,
copy_bytes,
create_memory_copy,
dummy_node_for_type,
Expand Down Expand Up @@ -1560,9 +1560,9 @@ def _create_ir(value, buf, length, salt, revert_on_failure=True):
if not revert_on_failure:
return ret

ret = clamp_nonzero(ret)
ret.set_error_msg(f"{create_op} failed")
return ret
with ret.cache_when_complex("addr") as (b1, addr):
ret = IRnode.from_list(["seq", check_create_operation(addr), addr])
return b1.resolve(ret)


# calculate the gas used by create for a given number of bytes
Expand Down
11 changes: 11 additions & 0 deletions vyper/codegen/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ def check_external_call(call_ir):
return ["if", ["iszero", call_ir], propagate_revert_ir]


# propagate revert message when create operations fail
# note the code for this is substantially the same as check_external_call,
# but keep it separate in case the assumptions about CREATE change.
def check_create_operation(create_ir: IRnode):
copy_revertdata = ["returndatacopy", 0, 0, "returndatasize"]
revert = IRnode.from_list(["revert", 0, "returndatasize"], error_msg="create failed")

propagate_revert_ir = ["seq", copy_revertdata, revert]
return ["if", ["iszero", create_ir], propagate_revert_ir]


# cost per byte of the identity precompile
def _identity_gas_bound(num_bytes):
return GAS_IDENTITY + GAS_IDENTITYWORD * (ceil32(num_bytes) // 32)
Expand Down