diff --git a/evmc b/evmc index 224080ef8c8..7a3f6bb57a7 160000 --- a/evmc +++ b/evmc @@ -1 +1 @@ -Subproject commit 224080ef8c8d99f5cfe59067a3bd995be23349bf +Subproject commit 7a3f6bb57a7d125ab5813b5ec4338a3959a3a7d7 diff --git a/libaleth-interpreter/VM.cpp b/libaleth-interpreter/VM.cpp index 9ab9c0ca01c..2c12019dfe7 100644 --- a/libaleth-interpreter/VM.cpp +++ b/libaleth-interpreter/VM.cpp @@ -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(); } @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -1066,7 +1063,7 @@ void VM::interpretCases() m_SP[2] > codeOffsetMax ? codeOffsetMax : static_cast(m_SP[2]); size_t size = static_cast(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); @@ -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 @@ -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 @@ -1356,22 +1353,25 @@ void VM::interpretCases() if (m_message->flags & EVMC_STATIC) throwDisallowedStateChange(); - static_assert( - 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 diff --git a/libaleth-interpreter/VM.h b/libaleth-interpreter/VM.h index 6a34c0c2912..30f20bfe6fa 100644 --- a/libaleth-interpreter/VM.h +++ b/libaleth-interpreter/VM.h @@ -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; diff --git a/libaleth-interpreter/VMCalls.cpp b/libaleth-interpreter/VMCalls.cpp index f797bf3bf15..881fbf8a04f 100644 --- a/libaleth-interpreter/VMCalls.cpp +++ b/libaleth-interpreter/VMCalls.cpp @@ -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) { @@ -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)); @@ -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); @@ -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) { @@ -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; } diff --git a/libevm/ExtVMFace.cpp b/libevm/ExtVMFace.cpp index 41b875957c1..2c60fb29e4f 100644 --- a/libevm/ExtVMFace.cpp +++ b/libevm/ExtVMFace.cpp @@ -58,23 +58,49 @@ evmc_storage_status setStorage(evmc_context* _context, evmc_address const* _addr (void)_addr; auto& env = static_cast(*_context); assert(fromEvmC(*_addr) == env.myAddress); - u256 index = fromEvmC(*_key); - u256 value = fromEvmC(*_value); - u256 oldValue = env.store(index); + u256 const index = fromEvmC(*_key); + u256 const newValue = fromEvmC(*_value); + u256 const currentValue = env.store(index); - if (value == oldValue) + if (newValue == currentValue) return EVMC_STORAGE_UNCHANGED; + EVMSchedule const& schedule = env.evmSchedule(); auto status = EVMC_STORAGE_MODIFIED; - if (oldValue == 0) - status = EVMC_STORAGE_ADDED; - else if (value == 0) + u256 const originalValue = env.originalStorageValue(index); + if (originalValue == currentValue || !schedule.eip1283Mode) { - status = EVMC_STORAGE_DELETED; - env.sub.refunds += env.evmSchedule().sstoreRefundGas; + if (currentValue == 0) + status = EVMC_STORAGE_ADDED; + else if (newValue == 0) + { + status = EVMC_STORAGE_DELETED; + env.sub.refunds += schedule.sstoreRefundGas; + } + } + else + { + status = EVMC_STORAGE_MODIFIED_DIRTY; + if (originalValue != 0) + { + if (currentValue == 0) + { + assert(env.sub.refunds >= schedule.sstoreRefundGas); + env.sub.refunds -= schedule.sstoreRefundGas; + } + if (newValue == 0) + env.sub.refunds += schedule.sstoreRefundGas; + } + if (originalValue == newValue) + { + if (originalValue == 0) + env.sub.refunds += schedule.sstoreRefundGas + schedule.sstoreRefundNonzeroGas; + else + env.sub.refunds += schedule.sstoreRefundNonzeroGas; + } } - env.setStore(index, value); // Interface uses native endianness + env.setStore(index, newValue); // Interface uses native endianness return status; } @@ -148,16 +174,18 @@ void log( bytesConstRef{_data, _dataSize}); } -void getTxContext(evmc_tx_context* result, evmc_context* _context) noexcept +evmc_tx_context getTxContext(evmc_context* _context) noexcept { auto& env = static_cast(*_context); - result->tx_gas_price = toEvmC(env.gasPrice); - result->tx_origin = toEvmC(env.origin); - result->block_coinbase = toEvmC(env.envInfo().author()); - result->block_number = static_cast(env.envInfo().number()); - result->block_timestamp = static_cast(env.envInfo().timestamp()); - result->block_gas_limit = static_cast(env.envInfo().gasLimit()); - result->block_difficulty = toEvmC(env.envInfo().difficulty()); + evmc_tx_context result = {}; + result.tx_gas_price = toEvmC(env.gasPrice); + result.tx_origin = toEvmC(env.origin); + result.block_coinbase = toEvmC(env.envInfo().author()); + result.block_number = static_cast(env.envInfo().number()); + result.block_timestamp = static_cast(env.envInfo().timestamp()); + result.block_gas_limit = static_cast(env.envInfo().gasLimit()); + result.block_difficulty = toEvmC(env.envInfo().difficulty()); + return result; } void getBlockHash(evmc_uint256be* o_hash, evmc_context* _envPtr, int64_t _number) @@ -166,7 +194,7 @@ void getBlockHash(evmc_uint256be* o_hash, evmc_context* _envPtr, int64_t _number *o_hash = toEvmC(env.blockHash(_number)); } -void create(evmc_result* o_result, ExtVMFace& _env, evmc_message const* _msg) noexcept +evmc_result create(ExtVMFace& _env, evmc_message const* _msg) noexcept { u256 gas = _msg->gas; u256 value = fromEvmC(_msg->value); @@ -178,16 +206,12 @@ void create(evmc_result* o_result, ExtVMFace& _env, evmc_message const* _msg) no assert(fromEvmC(_msg->sender) == _env.myAddress); CreateResult result = _env.create(value, gas, init, opcode, salt, {}); - o_result->status_code = result.status; - o_result->gas_left = static_cast(gas); - o_result->release = nullptr; + evmc_result evmcResult = {}; + evmcResult.status_code = result.status; + evmcResult.gas_left = static_cast(gas); if (result.status == EVMC_SUCCESS) - { - o_result->create_address = toEvmC(result.address); - o_result->output_data = nullptr; - o_result->output_size = 0; - } + evmcResult.create_address = toEvmC(result.address); else { // Pass the output to the EVM without a copy. The EVM will delete it @@ -195,15 +219,15 @@ void create(evmc_result* o_result, ExtVMFace& _env, evmc_message const* _msg) no // First assign reference. References are not invalidated when vector // of bytes is moved. See `.takeBytes()` below. - o_result->output_data = result.output.data(); - o_result->output_size = result.output.size(); + evmcResult.output_data = result.output.data(); + evmcResult.output_size = result.output.size(); // Place a new vector of bytes containing output in result's reserved memory. - auto* data = evmc_get_optional_storage(o_result); + auto* data = evmc_get_optional_storage(&evmcResult); static_assert(sizeof(bytes) <= sizeof(*data), "Vector is too big"); new(data) bytes(result.output.takeBytes()); // Set the destructor to delete the vector. - o_result->release = [](evmc_result const* _result) + evmcResult.release = [](evmc_result const* _result) { auto* data = evmc_get_const_optional_storage(_result); auto& output = reinterpret_cast(*data); @@ -212,16 +236,17 @@ void create(evmc_result* o_result, ExtVMFace& _env, evmc_message const* _msg) no output.~bytes(); }; } + return evmcResult; } -void call(evmc_result* o_result, evmc_context* _context, evmc_message const* _msg) noexcept +evmc_result call(evmc_context* _context, evmc_message const* _msg) noexcept { assert(_msg->gas >= 0 && "Invalid gas value"); auto& env = static_cast(*_context); // Handle CREATE separately. if (_msg->kind == EVMC_CREATE || _msg->kind == EVMC_CREATE2) - return create(o_result, env, _msg); + return create(env, _msg); CallParameters params; params.gas = _msg->gas; @@ -237,23 +262,24 @@ void call(evmc_result* o_result, evmc_context* _context, evmc_message const* _ms params.onOp = {}; CallResult result = env.call(params); - o_result->status_code = result.status; - o_result->gas_left = static_cast(params.gas); + evmc_result evmcResult = {}; + evmcResult.status_code = result.status; + evmcResult.gas_left = static_cast(params.gas); // Pass the output to the EVM without a copy. The EVM will delete it // when finished with it. // First assign reference. References are not invalidated when vector // of bytes is moved. See `.takeBytes()` below. - o_result->output_data = result.output.data(); - o_result->output_size = result.output.size(); + evmcResult.output_data = result.output.data(); + evmcResult.output_size = result.output.size(); // Place a new vector of bytes containing output in result's reserved memory. - auto* data = evmc_get_optional_storage(o_result); + auto* data = evmc_get_optional_storage(&evmcResult); static_assert(sizeof(bytes) <= sizeof(*data), "Vector is too big"); new(data) bytes(result.output.takeBytes()); // Set the destructor to delete the vector. - o_result->release = [](evmc_result const* _result) + evmcResult.release = [](evmc_result const* _result) { auto* data = evmc_get_const_optional_storage(_result); auto& output = reinterpret_cast(*data); @@ -261,9 +287,10 @@ void call(evmc_result* o_result, evmc_context* _context, evmc_message const* _ms // This is normal pattern when placement new operator is used. output.~bytes(); }; + return evmcResult; } -evmc_context_fn_table const fnTable = { +evmc_host_interface const hostInterface = { accountExists, getStorage, setStorage, @@ -282,7 +309,7 @@ evmc_context_fn_table const fnTable = { ExtVMFace::ExtVMFace(EnvInfo const& _envInfo, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes _code, h256 const& _codeHash, unsigned _depth, bool _isCreate, bool _staticCall) - : evmc_context{&fnTable}, + : evmc_context{&hostInterface}, m_envInfo(_envInfo), myAddress(_myAddress), caller(_caller), diff --git a/test/unittests/libevm/VMTest.cpp b/test/unittests/libevm/VMTest.cpp index bf9263a40d0..d4ce8f54062 100644 --- a/test/unittests/libevm/VMTest.cpp +++ b/test/unittests/libevm/VMTest.cpp @@ -426,6 +426,12 @@ class LegacyVMSstoreTestFixture : public SstoreTestFixture LegacyVMSstoreTestFixture() : SstoreTestFixture{new LegacyVM} {} }; +class AlethInterpreterSstoreTestFixture : public SstoreTestFixture +{ +public: + AlethInterpreterSstoreTestFixture() : SstoreTestFixture{new EVMC{evmc_create_interpreter()}} {} +}; + } // namespace BOOST_FIXTURE_TEST_SUITE(LegacyVMSuite, TestOutputHelperFixture) @@ -671,4 +677,93 @@ BOOST_AUTO_TEST_CASE(AlethInterpreterExtCodeHashIgnoresHigh12Bytes) BOOST_AUTO_TEST_SUITE_END() +BOOST_FIXTURE_TEST_SUITE(AlethInterpreterSstoreSuite, AlethInterpreterSstoreTestFixture) + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case1) +{ + testEip1283Case1(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case2) +{ + testEip1283Case2(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case3) +{ + testEip1283Case3(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case4) +{ + testEip1283Case4(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case5) +{ + testEip1283Case5(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case6) +{ + testEip1283Case6(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case7) +{ + testEip1283Case7(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case8) +{ + testEip1283Case8(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case9) +{ + testEip1283Case9(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case10) +{ + testEip1283Case10(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case11) +{ + testEip1283Case11(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case12) +{ + testEip1283Case12(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case13) +{ + testEip1283Case13(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case14) +{ + testEip1283Case14(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case15) +{ + testEip1283Case15(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case16) +{ + testEip1283Case16(); +} + +BOOST_AUTO_TEST_CASE(AlethInterpreterSstoreEip1283Case17) +{ + testEip1283Case17(); +} + +BOOST_AUTO_TEST_SUITE_END() + BOOST_AUTO_TEST_SUITE_END()