Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify the difference between code and recipient #286

Merged
merged 5 commits into from
Jul 13, 2021
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
50 changes: 33 additions & 17 deletions core/silkworm/execution/evm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,62 +154,63 @@ evmc::result EVM::create(const evmc_message& message) noexcept {
evmc::result EVM::call(const evmc_message& message) noexcept {
evmc::result res{EVMC_SUCCESS, message.gas, nullptr, 0};

auto value{intx::be::load<intx::uint256>(message.value)};
const auto value{intx::be::load<intx::uint256>(message.value)};
if (message.kind != EVMC_DELEGATECALL && state_.get_balance(message.sender) < value) {
res.status_code = EVMC_INSUFFICIENT_BALANCE;
return res;
}

const bool precompiled{is_precompiled(message.destination)};
// See Section 8 "Message Call" of the Yellow Paper for the difference between code & recipient.
// destination in evmc_message can mean either code or recipient, depending on the context.
const evmc_address code_address{message.destination};
const evmc_address recipient_address{recipient_of_call_message(message)};

const bool precompiled{is_precompiled(code_address)};
const evmc_revision rev{revision()};

// https://eips.ethereum.org/EIPS/eip-161
if (value == 0 && rev >= EVMC_SPURIOUS_DRAGON && !state_.exists(message.destination) && !precompiled) {
if (value == 0 && rev >= EVMC_SPURIOUS_DRAGON && !precompiled && !state_.exists(code_address)) {
return res;
}

auto snapshot{state_.take_snapshot()};
const auto snapshot{state_.take_snapshot()};

if (message.kind == EVMC_CALL) {
if (message.flags & EVMC_STATIC) {
// Match geth logic
// https://github.com/ethereum/go-ethereum/blob/v1.9.25/core/vm/evm.go#L391
state_.touch(message.destination);
state_.touch(recipient_address);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be fixing a consensus bug. Are there any tests failing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be fixing a consensus bug. Are there any tests failing?

Not really because this code is executed only when message.kind == EVMC_CALL

} else {
state_.subtract_from_balance(message.sender, value);
state_.add_to_balance(message.destination, value);
state_.add_to_balance(recipient_address, value);
}
}

if (precompiled) {
uint8_t num{message.destination.bytes[kAddressLength - 1]};
const uint8_t num{code_address.bytes[kAddressLength - 1]};
precompiled::Contract contract{precompiled::kContracts[num - 1]};
ByteView input{message.input_data, message.input_size};
int64_t gas = contract.gas(input, revision());
const ByteView input{message.input_data, message.input_size};
const int64_t gas = contract.gas(input, revision());
if (gas < 0 || gas > message.gas) {
res.status_code = EVMC_OUT_OF_GAS;
} else {
std::optional<Bytes> output{contract.run(input)};
const std::optional<Bytes> output{contract.run(input)};
if (output) {
res = {EVMC_SUCCESS, message.gas - gas, output->data(), output->size()};
} else {
res.status_code = EVMC_PRECOMPILE_FAILURE;
}
}
} else {
ByteView code{state_.get_code(message.destination)};
const ByteView code{state_.get_code(code_address)};
if (code.empty()) {
return res;
}

evmc::bytes32 code_hash{state_.get_code_hash(message.destination)};
const evmc::bytes32 code_hash{state_.get_code_hash(code_address)};

evmc_message msg{message};
if (msg.kind == EVMC_CALLCODE) {
msg.destination = msg.sender;
} else if (msg.kind == EVMC_DELEGATECALL) {
msg.destination = address_stack_.top();
}
msg.destination = recipient_address;

res = execute(msg, code, code_hash);
}
Expand All @@ -224,7 +225,22 @@ evmc::result EVM::call(const evmc_message& message) noexcept {
return res;
}

evmc_address EVM::recipient_of_call_message(const evmc_message& message) noexcept {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is similar code for it in Aleth: https://github.com/ethereum/aleth/blob/master/libevm/ExtVMFace.cpp#L206-L208

I'm usually confused by this, but it seems that for the CALLCODE the answer is also message.destination.

if (message.kind == EVMC_CALLCODE) {
return message.sender;
} else if (message.kind == EVMC_DELEGATECALL) {
// An evmc_message contains only two addresses (sender and "destination").
// However, in case of DELEGATECALL we need 3 addresses (sender, code, and recipient),
// so we recover the missing recipient address from the address_stack_.
return address_stack_.top();
} else {
assert(message.kind == EVMC_CALL);
return message.destination;
}
}

evmc::result EVM::execute(const evmc_message& msg, ByteView code, std::optional<evmc::bytes32> code_hash) noexcept {
// msg.destination here means recipient (what ADDRESS opcode would return)
address_stack_.push(msg.destination);

const evmc_revision rev{revision()};
Expand Down
2 changes: 2 additions & 0 deletions core/silkworm/execution/evm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ class EVM {

evmc::result call(const evmc_message& message) noexcept;

evmc_address recipient_of_call_message(const evmc_message& message) noexcept;

evmc::result execute(const evmc_message& message, ByteView code, std::optional<evmc::bytes32> code_hash) noexcept;

evmc_result execute_with_baseline_interpreter(evmc_revision rev, const evmc_message& message,
Expand Down