Skip to content

Commit cef84e8

Browse files
feat[lang]: bubble up create revertdata (#4540)
failed creates now bubble up revert data.
1 parent 206a119 commit cef84e8

File tree

3 files changed

+60
-4
lines changed

3 files changed

+60
-4
lines changed

tests/functional/builtins/codegen/test_create_functions.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,51 @@ def deploy_from_calldata(s: Bytes[1024], arg: uint256, salt: bytes32) -> address
862862
assert env.get_code(res) == runtime
863863

864864

865+
# test that create_from_blueprint bubbles up revert data
866+
def test_bubble_revert_data_blueprint(get_contract, tx_failed, deploy_blueprint_for):
867+
ctor_code = """
868+
@deploy
869+
def __init__():
870+
raise "bad ctor"
871+
"""
872+
873+
f, _ = deploy_blueprint_for(ctor_code)
874+
875+
deployer_code = """
876+
@external
877+
def deploy_from_address(t: address) -> address:
878+
return create_from_blueprint(t)
879+
"""
880+
881+
deployer = get_contract(deployer_code)
882+
883+
with tx_failed(exc_text="bad ctor"):
884+
deployer.deploy_from_address(f.address)
885+
886+
887+
# test that raw_create bubbles up revert data
888+
def test_bubble_revert_data_raw_create(get_contract, tx_failed):
889+
to_deploy_code = """
890+
@deploy
891+
def __init__():
892+
raise "bad ctor"
893+
"""
894+
895+
out = compile_code(to_deploy_code, output_formats=["bytecode"])
896+
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x"))
897+
898+
deployer_code = """
899+
@external
900+
def deploy_from_calldata(s: Bytes[1024]) -> address:
901+
return raw_create(s)
902+
"""
903+
904+
deployer = get_contract(deployer_code)
905+
906+
with tx_failed(exc_text="bad ctor"):
907+
deployer.deploy_from_calldata(initcode)
908+
909+
865910
# test raw_create with all combinations of value and revert_on_failure kwargs
866911
# (including not present at all)
867912
# additionally parametrize whether the constructor reverts or not

vyper/builtins/functions.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
bytes_data_ptr,
1616
calculate_type_for_external_return,
1717
check_buffer_overflow_ir,
18+
check_create_operation,
1819
check_external_call,
1920
clamp,
2021
clamp2,
2122
clamp_basetype,
22-
clamp_nonzero,
2323
copy_bytes,
2424
create_memory_copy,
2525
dummy_node_for_type,
@@ -1560,9 +1560,9 @@ def _create_ir(value, buf, length, salt, revert_on_failure=True):
15601560
if not revert_on_failure:
15611561
return ret
15621562

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

15671567

15681568
# calculate the gas used by create for a given number of bytes

vyper/codegen/core.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,17 @@ def check_external_call(call_ir):
9191
return ["if", ["iszero", call_ir], propagate_revert_ir]
9292

9393

94+
# propagate revert message when create operations fail
95+
# note the code for this is substantially the same as check_external_call,
96+
# but keep it separate in case the assumptions about CREATE change.
97+
def check_create_operation(create_ir: IRnode):
98+
copy_revertdata = ["returndatacopy", 0, 0, "returndatasize"]
99+
revert = IRnode.from_list(["revert", 0, "returndatasize"], error_msg="create failed")
100+
101+
propagate_revert_ir = ["seq", copy_revertdata, revert]
102+
return ["if", ["iszero", create_ir], propagate_revert_ir]
103+
104+
94105
# cost per byte of the identity precompile
95106
def _identity_gas_bound(num_bytes):
96107
return GAS_IDENTITY + GAS_IDENTITYWORD * (ceil32(num_bytes) // 32)

0 commit comments

Comments
 (0)