Skip to content

Commit 96bf424

Browse files
authored
eof: Remove support for EOF Creation Transaction (#1190)
2 parents 17fe4a4 + 0355d0d commit 96bf424

10 files changed

+111
-516
lines changed

lib/evmone/eof.cpp

+73-73
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <numeric>
1717
#include <queue>
1818
#include <unordered_set>
19+
#include <variant>
1920
#include <vector>
2021

2122
namespace evmone
@@ -225,14 +226,84 @@ std::variant<EOFSectionHeaders, EOFValidationError> validate_section_headers(byt
225226
std::accumulate(section_headers.container_sizes.begin(),
226227
section_headers.container_sizes.end(), uint64_t{0});
227228
const auto remaining_container_size = static_cast<uint64_t>(container_end - it);
228-
// Only data section may be truncated, so remaining_container size must be at least
229-
// declared_size_without_data
229+
// Only data section may be truncated, so remaining_container size must be in
230+
// [declared_size_without_data, declared_size_without_data + declared_data_size]
230231
if (remaining_container_size < section_bodies_without_data)
231232
return EOFValidationError::invalid_section_bodies_size;
233+
if (remaining_container_size > section_bodies_without_data + section_headers.data_size)
234+
return EOFValidationError::invalid_section_bodies_size;
232235

233236
return section_headers;
234237
}
235238

239+
std::variant<EOF1Header, EOFValidationError> validate_header(
240+
evmc_revision rev, bytes_view container) noexcept
241+
{
242+
if (!is_eof_container(container))
243+
return EOFValidationError::invalid_prefix;
244+
245+
const auto version = get_eof_version(container);
246+
if (version != 1)
247+
return EOFValidationError::eof_version_unknown;
248+
249+
if (rev < EVMC_OSAKA)
250+
return EOFValidationError::eof_version_unknown;
251+
252+
// `offset` variable handled below is known to not be greater than the container size, as
253+
// checked in `validate_section_headers`. Combined with the requirement for the container
254+
// size to not exceed MAX_INITCODE_SIZE (checked before `validate-header` is called),
255+
// this allows us to cast `offset` to narrower integers.
256+
assert(container.size() <= MAX_INITCODE_SIZE);
257+
258+
auto section_headers_or_error = validate_section_headers(container);
259+
if (const auto* error = std::get_if<EOFValidationError>(&section_headers_or_error))
260+
return *error;
261+
262+
auto& section_headers = std::get<EOFSectionHeaders>(section_headers_or_error);
263+
264+
const auto header_size = eof_header_size(section_headers);
265+
266+
const auto type_section_offset = header_size;
267+
268+
if (section_headers.type_size !=
269+
section_headers.code_sizes.size() * EOF1Header::TYPE_ENTRY_SIZE)
270+
return EOFValidationError::invalid_type_section_size;
271+
272+
auto offset = header_size + section_headers.type_size;
273+
274+
std::vector<uint16_t> code_offsets;
275+
code_offsets.reserve(section_headers.code_sizes.size());
276+
for (const auto code_size : section_headers.code_sizes)
277+
{
278+
assert(offset <= std::numeric_limits<uint16_t>::max());
279+
code_offsets.emplace_back(static_cast<uint16_t>(offset));
280+
offset += code_size;
281+
}
282+
283+
std::vector<uint32_t> container_offsets;
284+
container_offsets.reserve(section_headers.container_sizes.size());
285+
for (const auto container_size : section_headers.container_sizes)
286+
{
287+
assert(offset <= std::numeric_limits<uint32_t>::max());
288+
container_offsets.emplace_back(static_cast<uint32_t>(offset));
289+
offset += container_size;
290+
}
291+
292+
assert(offset <= std::numeric_limits<uint32_t>::max());
293+
const auto data_offset = static_cast<uint32_t>(offset);
294+
295+
return EOF1Header{
296+
.version = container[2],
297+
.type_section_offset = type_section_offset,
298+
.code_sizes = std::move(section_headers.code_sizes),
299+
.code_offsets = std::move(code_offsets),
300+
.data_size = section_headers.data_size,
301+
.data_offset = data_offset,
302+
.container_sizes = std::move(section_headers.container_sizes),
303+
.container_offsets = std::move(container_offsets),
304+
};
305+
}
306+
236307
EOFValidationError validate_types(bytes_view container, const EOF1Header& header) noexcept
237308
{
238309
for (size_t i = 0; i < header.get_type_count(); ++i)
@@ -634,9 +705,6 @@ EOFValidationError validate_eof1(
634705

635706
auto& header = std::get<EOF1Header>(error_or_header);
636707

637-
if (container.size() > static_cast<size_t>(header.data_offset) + header.data_size)
638-
return EOFValidationError::invalid_section_bodies_size;
639-
640708
if (const auto err = validate_types(container, header); err != EOFValidationError::success)
641709
return err;
642710

@@ -756,74 +824,6 @@ bool is_eof_container(bytes_view container) noexcept
756824
return container.starts_with(EOF_MAGIC);
757825
}
758826

759-
std::variant<EOF1Header, EOFValidationError> validate_header(
760-
evmc_revision rev, bytes_view container) noexcept
761-
{
762-
if (!is_eof_container(container))
763-
return EOFValidationError::invalid_prefix;
764-
765-
const auto version = get_eof_version(container);
766-
if (version != 1)
767-
return EOFValidationError::eof_version_unknown;
768-
769-
if (rev < EVMC_OSAKA)
770-
return EOFValidationError::eof_version_unknown;
771-
772-
// `offset` variable handled below is known to not be greater than the container size, as
773-
// checked in `validate_section_headers`. Combined with the requirement for the container
774-
// size to not exceed MAX_INITCODE_SIZE (checked before `validate-header` is called),
775-
// this allows us to cast `offset` to narrower integers.
776-
assert(container.size() <= MAX_INITCODE_SIZE);
777-
778-
auto section_headers_or_error = validate_section_headers(container);
779-
if (const auto* error = std::get_if<EOFValidationError>(&section_headers_or_error))
780-
return *error;
781-
782-
auto& section_headers = std::get<EOFSectionHeaders>(section_headers_or_error);
783-
784-
const auto header_size = eof_header_size(section_headers);
785-
786-
const auto type_section_offset = header_size;
787-
788-
if (section_headers.type_size !=
789-
section_headers.code_sizes.size() * EOF1Header::TYPE_ENTRY_SIZE)
790-
return EOFValidationError::invalid_type_section_size;
791-
792-
auto offset = header_size + section_headers.type_size;
793-
794-
std::vector<uint16_t> code_offsets;
795-
code_offsets.reserve(section_headers.code_sizes.size());
796-
for (const auto code_size : section_headers.code_sizes)
797-
{
798-
assert(offset <= std::numeric_limits<uint16_t>::max());
799-
code_offsets.emplace_back(static_cast<uint16_t>(offset));
800-
offset += code_size;
801-
}
802-
803-
std::vector<uint32_t> container_offsets;
804-
container_offsets.reserve(section_headers.container_sizes.size());
805-
for (const auto container_size : section_headers.container_sizes)
806-
{
807-
assert(offset <= std::numeric_limits<uint32_t>::max());
808-
container_offsets.emplace_back(static_cast<uint32_t>(offset));
809-
offset += container_size;
810-
}
811-
812-
assert(offset <= std::numeric_limits<uint32_t>::max());
813-
const auto data_offset = static_cast<uint32_t>(offset);
814-
815-
return EOF1Header{
816-
.version = container[2],
817-
.type_section_offset = type_section_offset,
818-
.code_sizes = std::move(section_headers.code_sizes),
819-
.code_offsets = std::move(code_offsets),
820-
.data_size = section_headers.data_size,
821-
.data_offset = data_offset,
822-
.container_sizes = std::move(section_headers.container_sizes),
823-
.container_offsets = std::move(container_offsets),
824-
};
825-
}
826-
827827
/// This function expects the prefix and version to be valid, as it ignores it.
828828
EOF1Header read_valid_eof1_header(bytes_view container)
829829
{

lib/evmone/eof.hpp

+1-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include <cstddef>
1111
#include <cstdint>
1212
#include <string_view>
13-
#include <variant>
1413
#include <vector>
1514

1615
namespace evmone
@@ -203,7 +202,7 @@ enum class EOFValidationError
203202

204203
enum class ContainerKind : uint8_t
205204
{
206-
/// Container that uses RETURNCODE. Can be used by EOFCREATE/TXCREATE/Creation transaction.
205+
/// Container that uses RETURNCODE. Can be used by EOFCREATE/TXCREATE.
207206
initcode,
208207
/// Container that uses STOP/RETURN. Can be returned by RETURNCODE.
209208
runtime,
@@ -213,10 +212,6 @@ enum class ContainerKind : uint8_t
213212
/// If the prefix is missing or invalid, 0 is returned meaning legacy code.
214213
[[nodiscard]] uint8_t get_eof_version(bytes_view container) noexcept;
215214

216-
/// Validates the header and returns its representation if successful.
217-
[[nodiscard]] EVMC_EXPORT std::variant<EOF1Header, EOFValidationError> validate_header(
218-
evmc_revision rev, bytes_view container) noexcept;
219-
220215
/// Validates whether given container is a valid EOF according to the rules of given revision.
221216
[[nodiscard]] EVMC_EXPORT EOFValidationError validate_eof(
222217
evmc_revision rev, ContainerKind kind, bytes_view container) noexcept;

lib/evmone/instructions_calls.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "delegation.hpp"
66
#include "eof.hpp"
77
#include "instructions.hpp"
8+
#include <variant>
89

910
constexpr int64_t MIN_RETAINED_GAS = 5000;
1011
constexpr int64_t MIN_CALLEE_GAS = 2300;

test/state/errors.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ enum ErrorCode : int
2828
EMPTY_BLOB_HASHES_LIST,
2929
INVALID_BLOB_HASH_VERSION,
3030
BLOB_GAS_LIMIT_EXCEEDED,
31+
EOF_CREATION_TRANSACTION,
3132
CREATE_SET_CODE_TX,
3233
EMPTY_AUTHORIZATION_LIST,
3334
INIT_CODE_EMPTY,
@@ -81,6 +82,8 @@ inline const std::error_category& evmone_category() noexcept
8182
return "invalid blob hash version";
8283
case BLOB_GAS_LIMIT_EXCEEDED:
8384
return "blob gas limit exceeded";
85+
case EOF_CREATION_TRANSACTION:
86+
return "EOF initcode in creation transaction";
8487
case CREATE_SET_CODE_TX:
8588
return "set code transaction must not be a create transaction";
8689
case EMPTY_AUTHORIZATION_LIST:

test/state/host.cpp

+3-36
Original file line numberDiff line numberDiff line change
@@ -277,42 +277,9 @@ std::optional<evmc_message> Host::prepare_message(evmc_message msg) noexcept
277277
}
278278
else
279279
{
280-
assert(msg.kind == EVMC_EOFCREATE);
281-
const bytes_view input = {msg.input_data, msg.input_size};
282-
283-
// Indicator of an EOF creation tx - EVMC_EOFCREATE at depth 0.
284-
if (msg.depth == 0)
285-
{
286-
// Assert this is a legacy EOF creation tx.
287-
assert(is_eof_container(input));
288-
const auto header_or_error = validate_header(m_rev, input);
289-
290-
const auto* header = std::get_if<EOF1Header>(&header_or_error);
291-
if (header == nullptr)
292-
return {}; // Light early exception.
293-
294-
if (!header->has_full_data(msg.input_size))
295-
return {}; // Light early exception.
296-
297-
const auto container_size =
298-
static_cast<size_t>(header->data_offset) + header->data_size;
299-
// Follows from the header->can_init condition above.
300-
assert(container_size <= msg.input_size);
301-
302-
msg.code = msg.input_data;
303-
msg.code_size = container_size;
304-
msg.input_data = msg.input_data + container_size;
305-
msg.input_size = msg.input_size - container_size;
306-
307-
if (validate_eof(m_rev, ContainerKind::initcode, {msg.code, msg.code_size}) !=
308-
EOFValidationError::success)
309-
return {}; // Light early exception.
310-
311-
msg.recipient = compute_create_address(msg.sender, creation_sender_nonce);
312-
}
313280
// EOFCREATE or TXCREATE
314-
else
315-
msg.recipient = compute_eofcreate_address(msg.sender, msg.create2_salt);
281+
assert(msg.kind == EVMC_EOFCREATE);
282+
msg.recipient = compute_eofcreate_address(msg.sender, msg.create2_salt);
316283
}
317284

318285
// By EIP-2929, the access to new created address is never reverted.
@@ -395,7 +362,7 @@ evmc::Result Host::create(const evmc_message& msg) noexcept
395362
{
396363
if (m_rev >= EVMC_OSAKA)
397364
{
398-
// Only EOFCREATE/TXCREATE/EOF-creation-tx is allowed to deploy code starting with
365+
// Only EOFCREATE/TXCREATE is allowed to deploy code starting with
399366
// EF. It must be valid EOF, which was validated before execution.
400367
if (msg.kind != EVMC_EOFCREATE)
401368
return evmc::Result{EVMC_CONTRACT_VALIDATION_FAILURE};

test/state/state.cpp

+10-11
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ TransactionCost compute_tx_intrinsic_cost(evmc_revision rev, const Transaction&
7575
static constexpr auto INITCODE_WORD_COST = 2;
7676
static constexpr auto TOTAL_COST_FLOOR_PER_TOKEN = 10;
7777

78-
const auto is_create = !tx.to.has_value(); // Covers also EOF creation transactions.
78+
const auto is_create = !tx.to.has_value();
7979

8080
const auto create_cost = (is_create && rev >= EVMC_HOMESTEAD) ? TX_CREATE_COST : 0;
8181

@@ -186,17 +186,12 @@ int64_t process_authorization_list(
186186
return delegation_refund;
187187
}
188188

189-
evmc_message build_message(
190-
const Transaction& tx, int64_t execution_gas_limit, evmc_revision rev) noexcept
189+
evmc_message build_message(const Transaction& tx, int64_t execution_gas_limit) noexcept
191190
{
192191
const auto recipient = tx.to.has_value() ? *tx.to : evmc::address{};
193192

194-
const auto is_legacy_eof_create =
195-
rev >= EVMC_OSAKA && !tx.to.has_value() && is_eof_container(tx.data);
196-
197-
return {.kind = is_legacy_eof_create ? EVMC_EOFCREATE :
198-
tx.to.has_value() ? EVMC_CALL : // NOLINT(readability-avoid-nested-conditional-operator)
199-
EVMC_CREATE,
193+
return {
194+
.kind = tx.to.has_value() ? EVMC_CALL : EVMC_CREATE,
200195
.flags = 0,
201196
.depth = 0,
202197
.gas = execution_gas_limit,
@@ -208,7 +203,8 @@ evmc_message build_message(
208203
.create2_salt = {},
209204
.code_address = recipient,
210205
.code = nullptr,
211-
.code_size = 0};
206+
.code_size = 0,
207+
};
212208
}
213209
} // namespace
214210

@@ -531,6 +527,9 @@ std::variant<TransactionProperties, std::error_code> validate_transaction(
531527
if (rev >= EVMC_SHANGHAI && !tx.to.has_value() && tx.data.size() > MAX_INITCODE_SIZE)
532528
return make_error_code(INIT_CODE_SIZE_LIMIT_EXCEEDED);
533529

530+
if (rev >= EVMC_OSAKA && !tx.to.has_value() && is_eof_container(tx.data))
531+
return make_error_code(EOF_CREATION_TRANSACTION);
532+
534533
// Compute and check if sender has enough balance for the theoretical maximum transaction cost.
535534
// Note this is different from tx_max_cost computed with effective gas price later.
536535
// The computation cannot overflow if done with 512-bit precision.
@@ -634,7 +633,7 @@ TransactionReceipt transition(const StateView& state_view, const BlockInfo& bloc
634633
if (rev >= EVMC_SHANGHAI)
635634
host.access_account(block.coinbase);
636635

637-
auto message = build_message(tx, tx_props.execution_gas_limit, rev);
636+
auto message = build_message(tx, tx_props.execution_gas_limit);
638637
if (tx.to.has_value())
639638
{
640639
if (const auto delegate = get_delegate_address(host, *tx.to))

test/statetest/statetest_export.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ namespace evmone::test
4949
return "TR_BLOBVERSION_INVALID";
5050
case BLOB_GAS_LIMIT_EXCEEDED:
5151
return "TR_BLOBLIST_OVERSIZE";
52+
case EOF_CREATION_TRANSACTION:
53+
return "EOFException.EOFCreationTransaction";
5254
case UNKNOWN_ERROR:
5355
return "Unknown error";
5456
default:

0 commit comments

Comments
 (0)