Skip to content

Commit

Permalink
state: Fix failing code deployment in Frontier
Browse files Browse the repository at this point in the history
In Frontier when CREATE fails to deploy the code because of
the deployment cost the creation behaves as it has been successful
although the created account has empty code.

Fix the CREATE instruction by returning the address of the created
account instead of the null address.

The gas refund is also applied as in the case of successful
code deployment. Geth agrees with this but this situation probably
never happened on Mainnet.
  • Loading branch information
chfast committed Feb 28, 2024
1 parent 0de1555 commit 83e2efc
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 2 deletions.
5 changes: 3 additions & 2 deletions test/state/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,9 @@ evmc::Result Host::create(const evmc_message& msg) noexcept
gas_left -= cost;
if (gas_left < 0)
{
return (m_rev == EVMC_FRONTIER) ? evmc::Result{EVMC_SUCCESS, result.gas_left} :
evmc::Result{EVMC_FAILURE};
return (m_rev == EVMC_FRONTIER) ?
evmc::Result{EVMC_SUCCESS, result.gas_left, result.gas_refund, msg.recipient} :
evmc::Result{EVMC_FAILURE};
}

if (m_rev >= EVMC_PRAGUE && (is_eof_container(initcode) || is_eof_container(code)))
Expand Down
112 changes: 112 additions & 0 deletions test/unittests/state_transition_create_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,115 @@ TEST_F(state_transition, create2_max_nonce)

expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; // Nonce is unchanged.
}

TEST_F(state_transition, code_deployment_out_of_gas_tw)
{
rev = EVMC_TANGERINE_WHISTLE; // 63/64 gas rule enabled
block.base_fee = 0;
const auto initcode = ret(0, 5000); // create contract will a lot of zeros, deploy cost 1M

tx.to = To;
tx.gas_limit = 1000000;
pre.insert(To, {.code = mstore(0, push(initcode)) +
sstore(0, create().input(32 - initcode.size(), initcode.size()))});

expect.post[To].storage[0x00_bytes32] = 0x00_bytes32;
}

TEST_F(state_transition, code_deployment_out_of_gas_f)
{
rev = EVMC_FRONTIER;
block.base_fee = 0;
const auto initcode = ret(0, 1000); // create contract will a lot of zeros

tx.to = To;
tx.gas_limit = 100000;
pre.insert(To, {.code = mstore(0, push(initcode)) +
sstore(0, create().input(32 - initcode.size(), initcode.size()))});

const auto created = compute_create_address(To, pre.get(To).nonce);
expect.post[created].code = bytes{}; // code deployment failure creates empty account
expect.post[created].nonce = 0;
bytes32 ccc{};
std::copy(std::begin(created.bytes), std::end(created.bytes), &ccc.bytes[12]);
expect.post[To].storage[0x00_bytes32] = ccc; // address of created empty
}

TEST_F(state_transition, code_deployment_out_of_gas_storage_tw)
{
rev = EVMC_TANGERINE_WHISTLE; // 63/64 gas rule enabled
block.base_fee = 0;
const auto initcode = sstore(0, 1) // set storage
+ ret(0, 5000); // create contract will a lot of zeros

tx.to = To;
tx.gas_limit = 1000000;
pre.insert(To, {.code = mstore(0, push(initcode)) +
sstore(0, create().input(32 - initcode.size(), initcode.size()))});

expect.post[To].storage[0x00_bytes32] = 0x00_bytes32;
}

TEST_F(state_transition, code_deployment_out_of_gas_storage_f)
{
rev = EVMC_FRONTIER;
block.base_fee = 0;
const auto initcode = sstore(0, 1) // set storage
+ ret(0, 1000); // create contract will a lot of zeros

tx.to = To;
tx.gas_limit = 100000;
pre.insert(To, {.code = mstore(0, push(initcode)) +
sstore(0, create().input(32 - initcode.size(), initcode.size()))});

expect.post[To].exists = true;
const auto created = compute_create_address(To, pre.get(To).nonce);
expect.post[created].code = bytes{}; // code deployment failure creates empty account
expect.post[created].nonce = 0;
expect.post[created].storage[0x00_bytes32] = 0x01_bytes32; // storage stays
bytes32 ccc{};
std::copy(std::begin(created.bytes), std::end(created.bytes), &ccc.bytes[12]);
expect.post[To].storage[0x00_bytes32] = ccc;
expect.gas_used = 93134;
}

TEST_F(state_transition, code_deployment_out_of_gas_refund_tw)
{
rev = EVMC_TANGERINE_WHISTLE; // 63/64 gas rule enabled
block.base_fee = 0;
const auto initcode = sstore(0, 1) // set storage
+ sstore(0, 0) // gas refund
+ ret(0, 5000); // create contract will a lot of zeros

tx.to = To;
tx.gas_limit = 1000000;
pre.insert(To, {.code = mstore(0, push(initcode)) +
sstore(0, create().input(32 - initcode.size(), initcode.size()))});

expect.post[To].storage[0x00_bytes32] = 0x00_bytes32;
expect.gas_used = 990207;
}

TEST_F(state_transition, code_deployment_out_of_gas_refund_f)
{
rev = EVMC_FRONTIER;
block.base_fee = 0;
const auto initcode = sstore(0, 1) // set storage
+ sstore(0, 0) // gas refund
+ ret(0, 1000); // create contract will a lot of zeros

tx.to = To;
tx.gas_limit = 100000;
pre.insert(To, {.code = mstore(0, push(initcode)) +
sstore(0, create().input(32 - initcode.size(), initcode.size()))});

expect.post[To].exists = true;
const auto created = compute_create_address(To, pre.get(To).nonce);
expect.post[created].code = bytes{}; // code deployment failure creates empty account
expect.post[created].nonce = 0;
expect.post[created].storage[0x00_bytes32] = 0x00_bytes32;
bytes32 ccc{};
std::copy(std::begin(created.bytes), std::end(created.bytes), &ccc.bytes[12]);
expect.post[To].storage[0x00_bytes32] = ccc;
expect.gas_used = 83140;
}

0 comments on commit 83e2efc

Please sign in to comment.