Skip to content

Commit

Permalink
Outside-in refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
gumb0 committed Jun 6, 2024
1 parent cf09ff7 commit 51b7cf3
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 49 deletions.
72 changes: 25 additions & 47 deletions lib/evmone/eof.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,6 @@ std::variant<std::vector<EOFCodeType>, EOFValidationError> validate_types(
/// Result of validating instructions in a code section.
struct InstructionValidationResult
{
/// Current container kind, determined by opcodes (RETURN, STOP, RETURCONTRACT) that were
/// seen in this section or previously validated sections.
ContainerKind kind;
/// Pairs of (container_index, opcode) of all opcodes referencing subcontainers in this section.
std::vector<std::pair<uint8_t, Opcode>> subcontainer_references;
/// Set of accessed code section indices.
Expand Down Expand Up @@ -331,19 +328,17 @@ std::variant<InstructionValidationResult, EOFValidationError> validate_instructi

if (op == OP_RETURNCONTRACT)
{
if (kind == ContainerKind::runtime)
return EOFValidationError::ambiguous_container_kind;
kind = ContainerKind::initcode;
if (kind == ContainerKind::runtime || kind == ContainerKind::initcode_runtime)
return EOFValidationError::incompatible_container_kind;
}

subcontainer_references.emplace_back(container_idx, Opcode{op});
++i;
}
else if (op == OP_RETURN || op == OP_STOP)
{
if (kind == ContainerKind::initcode)
return EOFValidationError::ambiguous_container_kind;
kind = ContainerKind::runtime;
if (kind == ContainerKind::initcode || kind == ContainerKind::initcode_runtime)
return EOFValidationError::incompatible_container_kind;
}
else
i += instr::traits[op].immediate_size;
Expand All @@ -353,7 +348,7 @@ std::variant<InstructionValidationResult, EOFValidationError> validate_instructi
if (is_returning != declared_returning)
return EOFValidationError::invalid_non_returning_flag;

return InstructionValidationResult{kind, subcontainer_references, accessed_code_sections};
return InstructionValidationResult{subcontainer_references, accessed_code_sections};
}

/// Validates that that we don't rjump inside an instruction's immediate.
Expand Down Expand Up @@ -592,33 +587,21 @@ std::variant<EOFValidationError, int32_t> validate_max_stack_height(
}

EOFValidationError validate_eof1(
evmc_revision rev, ContainerKind kind, bytes_view main_container) noexcept
evmc_revision rev, ContainerKind main_container_kind, bytes_view main_container) noexcept
{
struct ContainerValidation
{
bytes_view bytes;
bool referenced_by_eofcreate = false;
bool referenced_by_returncontract = false;
ContainerKind kind;
};
// Queue of containers left to process
std::queue<ContainerValidation> container_queue;

// Main container required to be initcode is equivalent to requirement for container referenced
// by EOFCREATE.
// Main container required to be runtime is equivalent to requirement for container referenced
// by RETURNCONTRACT.
const bool main_container_referenced_by_eofcreate =
(kind == ContainerKind::initcode || kind == ContainerKind::initcode_runtime);
const bool main_container_referenced_by_returncontract =
(kind == ContainerKind::runtime || kind == ContainerKind::initcode_runtime);

container_queue.push({main_container, main_container_referenced_by_eofcreate,
main_container_referenced_by_returncontract});
container_queue.push({main_container, main_container_kind});

while (!container_queue.empty())
{
const auto& [container, referenced_by_eofcreate, referenced_by_returncontract] =
container_queue.front();
const auto& [container, container_kind] = container_queue.front();

// Validate header
auto error_or_header = validate_header(rev, container);
Expand All @@ -634,7 +617,6 @@ EOFValidationError validate_eof1(
std::vector<bool> visited_code_sections(header.code_sizes.size());
std::queue<uint16_t> code_sections_queue({0});

ContainerKind container_kind = ContainerKind::initcode_runtime;
const auto subcontainer_count = header.container_sizes.size();
std::vector<bool> subcontainer_referenced_by_eofcreate(subcontainer_count, false);
std::vector<bool> subcontainer_referenced_by_returncontract(subcontainer_count, false);
Expand All @@ -656,9 +638,8 @@ EOFValidationError validate_eof1(
std::get_if<EOFValidationError>(&instr_validation_result_or_error))
return *error;

const auto& [updated_container_kind, subcontainer_references, accessed_code_sections] =
const auto& [subcontainer_references, accessed_code_sections] =
std::get<InstructionValidationResult>(instr_validation_result_or_error);
container_kind = updated_container_kind;

// Mark what instructions referenced which subcontainers.
for (const auto& [index, opcode] : subcontainer_references)
Expand Down Expand Up @@ -692,31 +673,28 @@ EOFValidationError validate_eof1(
visited_code_sections.end())
return EOFValidationError::unreachable_code_sections;

if (referenced_by_eofcreate && !header.can_init(container.size()))
if ((container_kind == ContainerKind::initcode ||
container_kind == ContainerKind::initcode_runtime) &&
!header.can_init(container.size()))
return EOFValidationError::eofcreate_with_truncated_container;

// Check if container kind (which instructions it has inside) is compatible with
// instructions that reference it in parent container.
if (container_kind == ContainerKind::initcode)
{
if (referenced_by_returncontract)
return EOFValidationError::incompatible_container_kind;
}
else if (container_kind == ContainerKind::runtime)
{
if (referenced_by_eofcreate)
return EOFValidationError::incompatible_container_kind;
}

// TODO Validate whether subcontainer was referenced by any instruction

// Enqueue subcontainers
for (size_t subcont_idx = 0; subcont_idx < subcontainer_count; ++subcont_idx)
{
const bytes_view subcontainer{header.get_container(container, subcont_idx)};

container_queue.push({subcontainer, subcontainer_referenced_by_eofcreate[subcont_idx],
subcontainer_referenced_by_returncontract[subcont_idx]});
const bool eofcreate = subcontainer_referenced_by_eofcreate[subcont_idx];
const bool returncontract = subcontainer_referenced_by_returncontract[subcont_idx];

// TODO Validate whether subcontainer was referenced by any instruction

auto subcontainer_kind = ContainerKind::initcode_runtime;
if (!eofcreate)
subcontainer_kind = ContainerKind::runtime;
else if (!returncontract)
subcontainer_kind = ContainerKind::initcode;

container_queue.push({subcontainer, subcontainer_kind});
}

container_queue.pop();
Expand Down
4 changes: 2 additions & 2 deletions test/unittests/eof_validation_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1432,7 +1432,7 @@ TEST_F(eof_validation, eofcreate_stop_and_returncontract)
const auto factory_code = eofcreate() + OP_STOP;
const bytecode factory_container = eof_bytecode(factory_code, 4).container(initcontainer);

add_test_case(factory_container, EOFValidationError::ambiguous_container_kind);
add_test_case(factory_container, EOFValidationError::incompatible_container_kind);
}

TEST_F(eof_validation, eofcreate_return_and_returncontract)
Expand All @@ -1443,5 +1443,5 @@ TEST_F(eof_validation, eofcreate_return_and_returncontract)
const auto factory_code = eofcreate() + OP_STOP;
const bytecode factory_container = eof_bytecode(factory_code, 4).container(initcontainer);

add_test_case(factory_container, EOFValidationError::ambiguous_container_kind);
add_test_case(factory_container, EOFValidationError::incompatible_container_kind);
}

0 comments on commit 51b7cf3

Please sign in to comment.