Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

Implement SSTORE net gas metering in aleth-interpreter #5238

Merged
merged 4 commits into from
Sep 5, 2018
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
60 changes: 30 additions & 30 deletions libaleth-interpreter/VM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,7 @@ void VM::fetchInstruction()
evmc_tx_context const& VM::getTxContext()
{
if (!m_tx_context)
{
m_tx_context.emplace();
m_context->fn_table->get_tx_context(&m_tx_context.value(), m_context);
}
m_tx_context.emplace(m_context->host->get_tx_context(m_context));
return m_tx_context.value();
}

Expand Down Expand Up @@ -370,20 +367,20 @@ void VM::interpretCases()

// After EIP158 zero-value suicides do not have to pay account creation gas.
evmc_uint256be rawBalance;
m_context->fn_table->get_balance(&rawBalance, m_context, &m_message->destination);
m_context->host->get_balance(&rawBalance, m_context, &m_message->destination);
u256 balance = fromEvmC(rawBalance);
if (balance > 0 || m_rev < EVMC_SPURIOUS_DRAGON)
{
// After EIP150 hard fork charge additional cost of sending
// ethers to non-existing account.
int destinationExists =
m_context->fn_table->account_exists(m_context, &destination);
m_context->host->account_exists(m_context, &destination);
if (m_rev >= EVMC_TANGERINE_WHISTLE && !destinationExists)
m_runGas += VMSchedule::callNewAccount;
}

updateIOGas();
m_context->fn_table->selfdestruct(m_context, &m_message->destination, &destination);
m_context->host->selfdestruct(m_context, &m_message->destination, &destination);
m_bounce = nullptr;
}
BREAK
Expand Down Expand Up @@ -458,7 +455,7 @@ void VM::interpretCases()
uint8_t const* data = m_mem.data() + size_t(m_SP[0]);
size_t dataSize = size_t(m_SP[1]);

m_context->fn_table->emit_log(
m_context->host->emit_log(
m_context, &m_message->destination, data, dataSize, nullptr, 0);
}
NEXT
Expand All @@ -478,7 +475,7 @@ void VM::interpretCases()
evmc_uint256be topics[] = {toEvmC(m_SP[2])};
size_t numTopics = sizeof(topics) / sizeof(topics[0]);

m_context->fn_table->emit_log(
m_context->host->emit_log(
m_context, &m_message->destination, data, dataSize, topics, numTopics);
}
NEXT
Expand All @@ -498,7 +495,7 @@ void VM::interpretCases()
evmc_uint256be topics[] = {toEvmC(m_SP[2]), toEvmC(m_SP[3])};
size_t numTopics = sizeof(topics) / sizeof(topics[0]);

m_context->fn_table->emit_log(
m_context->host->emit_log(
m_context, &m_message->destination, data, dataSize, topics, numTopics);
}
NEXT
Expand All @@ -518,7 +515,7 @@ void VM::interpretCases()
evmc_uint256be topics[] = {toEvmC(m_SP[2]), toEvmC(m_SP[3]), toEvmC(m_SP[4])};
size_t numTopics = sizeof(topics) / sizeof(topics[0]);

m_context->fn_table->emit_log(
m_context->host->emit_log(
m_context, &m_message->destination, data, dataSize, topics, numTopics);
}
NEXT
Expand All @@ -539,7 +536,7 @@ void VM::interpretCases()
toEvmC(m_SP[2]), toEvmC(m_SP[3]), toEvmC(m_SP[4]), toEvmC(m_SP[5])};
size_t numTopics = sizeof(topics) / sizeof(topics[0]);

m_context->fn_table->emit_log(
m_context->host->emit_log(
m_context, &m_message->destination, data, dataSize, topics, numTopics);
}
NEXT
Expand Down Expand Up @@ -904,7 +901,7 @@ void VM::interpretCases()

evmc_address address = toEvmC(asAddress(m_SP[0]));
evmc_uint256be rawBalance;
m_context->fn_table->get_balance(&rawBalance, m_context, &address);
m_context->host->get_balance(&rawBalance, m_context, &address);
m_SPP[0] = fromEvmC(rawBalance);
}
NEXT
Expand Down Expand Up @@ -989,7 +986,7 @@ void VM::interpretCases()

evmc_address address = toEvmC(asAddress(m_SP[0]));

m_SPP[0] = m_context->fn_table->get_code_size(m_context, &address);
m_SPP[0] = m_context->host->get_code_size(m_context, &address);
}
NEXT

Expand Down Expand Up @@ -1033,7 +1030,7 @@ void VM::interpretCases()
evmc_address address = toEvmC(asAddress(m_SP[0]));

evmc_uint256be hash;
m_context->fn_table->get_code_hash(&hash, m_context, &address);
m_context->host->get_code_hash(&hash, m_context, &address);
m_SPP[0] = fromEvmC(hash);
}
NEXT
Expand Down Expand Up @@ -1066,7 +1063,7 @@ void VM::interpretCases()
m_SP[2] > codeOffsetMax ? codeOffsetMax : static_cast<size_t>(m_SP[2]);
size_t size = static_cast<size_t>(copyMemSize);

size_t numCopied = m_context->fn_table->copy_code(
size_t numCopied = m_context->host->copy_code(
m_context, &address, codeOffset, &m_mem[memoryOffset], size);

std::fill_n(&m_mem[memoryOffset + numCopied], size - numCopied, 0);
Expand Down Expand Up @@ -1095,7 +1092,7 @@ void VM::interpretCases()
if (number < blockNumber && number >= std::max(int64_t(256), blockNumber) - 256)
{
evmc_uint256be hash;
m_context->fn_table->get_block_hash(&hash, m_context, int64_t(number));
m_context->host->get_block_hash(&hash, m_context, int64_t(number));
m_SPP[0] = fromEvmC(hash);
}
else
Expand Down Expand Up @@ -1345,7 +1342,7 @@ void VM::interpretCases()

evmc_uint256be key = toEvmC(m_SP[0]);
evmc_uint256be value;
m_context->fn_table->get_storage(&value, m_context, &m_message->destination, &key);
m_context->host->get_storage(&value, m_context, &m_message->destination, &key);
m_SPP[0] = fromEvmC(value);
}
NEXT
Expand All @@ -1356,22 +1353,25 @@ void VM::interpretCases()
if (m_message->flags & EVMC_STATIC)
throwDisallowedStateChange();

static_assert(
Copy link
Member Author

@gumb0 gumb0 Sep 3, 2018

Choose a reason for hiding this comment

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

For the sake of simplicity I got rid of the trick to charge sstoreSetGas before set_storage`.

To keep it I think we would need to handle pre-Constantinople as an additional special case. In Constantinople we can't know before actually calling set_storage whether the change will require DB write, or will be just a dirty write (unless we decide to provide access to "original storage values" to EVM)

Copy link
Member Author

Choose a reason for hiding this comment

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

Now I'm not sure if this is much simpler because it turns out we still need to handle Constantinople here differently (difference in the cost of X->X)
Here's the version with Constantinople as a separate case and keeping the old optimization: #5240

Copy link
Member

Choose a reason for hiding this comment

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

I think to be safe we should charge more/less the SLOAD cost, so sstoreUnchangedGas is good candidate and handles both pre and post Constantinople cases.
We want to make sure the sstoreUnchangedGas is the smallest from all SSTORE possible costs. Would be nice to use std::min here but it started be a constexpr function in C++14.

VMSchedule::sstoreResetGas <= VMSchedule::sstoreSetGas, "Wrong SSTORE gas costs");
m_runGas = VMSchedule::sstoreResetGas; // Charge the modification cost up front.
updateIOGas();

evmc_uint256be key = toEvmC(m_SP[0]);
evmc_uint256be value = toEvmC(m_SP[1]);
auto status =
m_context->fn_table->set_storage(m_context, &m_message->destination, &key, &value);
evmc_uint256be const key = toEvmC(m_SP[0]);
evmc_uint256be const value = toEvmC(m_SP[1]);
auto const status =
m_context->host->set_storage(m_context, &m_message->destination, &key, &value);

if (status == EVMC_STORAGE_ADDED)
m_runGas = VMSchedule::sstoreSetGas;
else if (status == EVMC_STORAGE_MODIFIED || status == EVMC_STORAGE_DELETED)
m_runGas = VMSchedule::sstoreResetGas;
else if (status == EVMC_STORAGE_UNCHANGED && m_rev < EVMC_CONSTANTINOPLE)
m_runGas = VMSchedule::sstoreResetGas;
else
{
// Charge additional amount for added storage item.
m_runGas = VMSchedule::sstoreSetGas - VMSchedule::sstoreResetGas;
updateIOGas();
assert(status == EVMC_STORAGE_UNCHANGED || status == EVMC_STORAGE_MODIFIED_DIRTY);
assert(m_rev >= EVMC_CONSTANTINOPLE);
m_runGas = VMSchedule::sstoreUnchangedGas;
}

updateIOGas();
}
NEXT

Expand Down
1 change: 1 addition & 0 deletions libaleth-interpreter/VM.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ struct VMSchedule
static constexpr int64_t sloadGas = 50;
static constexpr int64_t sstoreSetGas = 20000;
static constexpr int64_t sstoreResetGas = 5000;
static constexpr int64_t sstoreUnchangedGas = 200;
static constexpr int64_t jumpdestGas = 1;
static constexpr int64_t logGas = 375;
static constexpr int64_t logDataGas = 8;
Expand Down
12 changes: 5 additions & 7 deletions libaleth-interpreter/VMCalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ void VM::caseCreate()
m_returnData.clear();

evmc_uint256be rawBalance;
m_context->fn_table->get_balance(&rawBalance, m_context, &m_message->destination);
m_context->host->get_balance(&rawBalance, m_context, &m_message->destination);
u256 balance = fromEvmC(rawBalance);
if (balance >= endowment && m_message->depth < 1024)
{
Expand All @@ -146,8 +146,7 @@ void VM::caseCreate()
msg.kind = m_OP == Instruction::CREATE ? EVMC_CREATE : EVMC_CREATE2; // FIXME: In EVMC move the kind to the top.
msg.value = toEvmC(endowment);

evmc_result result;
m_context->fn_table->call(&result, m_context, &msg);
evmc_result result = m_context->host->call(m_context, &msg);

if (result.status_code == EVMC_SUCCESS)
m_SPP[0] = fromAddress(fromEvmC(result.create_address));
Expand Down Expand Up @@ -177,8 +176,7 @@ void VM::caseCall()
bytesRef output;
if (caseCallSetup(msg, output))
{
evmc_result result;
m_context->fn_table->call(&result, m_context, &msg);
evmc_result result = m_context->host->call(m_context, &msg);

m_returnData.assign(result.output_data, result.output_data + result.output_size);
bytesConstRef{&m_returnData}.copyTo(output);
Expand Down Expand Up @@ -222,7 +220,7 @@ bool VM::caseCallSetup(evmc_message& o_msg, bytesRef& o_output)
bool const haveValueArg = m_OP == Instruction::CALL || m_OP == Instruction::CALLCODE;

evmc_address destination = toEvmC(asAddress(m_SP[1]));
int destinationExists = m_context->fn_table->account_exists(m_context, &destination);
int destinationExists = m_context->host->account_exists(m_context, &destination);

if (m_OP == Instruction::CALL && !destinationExists)
{
Expand Down Expand Up @@ -273,7 +271,7 @@ bool VM::caseCallSetup(evmc_message& o_msg, bytesRef& o_output)
o_msg.gas += VMSchedule::callStipend;
{
evmc_uint256be rawBalance;
m_context->fn_table->get_balance(&rawBalance, m_context, &m_message->destination);
m_context->host->get_balance(&rawBalance, m_context, &m_message->destination);
u256 balance = fromEvmC(rawBalance);
balanceOk = balance >= value;
}
Expand Down
Loading