mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
216 lines
6.4 KiB
C++
216 lines
6.4 KiB
C++
/*
|
|
This file is part of solidity.
|
|
|
|
solidity is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
solidity is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/**
|
|
* EVM execution host, i.e. component that implements a simulated Ethereum blockchain
|
|
* for testing purposes.
|
|
*/
|
|
|
|
#include <test/EVMHost.h>
|
|
|
|
#include <test/evmc/helpers.hpp>
|
|
#include <test/evmc/loader.h>
|
|
|
|
#include <libdevcore/Exceptions.h>
|
|
#include <libdevcore/Assertions.h>
|
|
#include <libdevcore/Keccak256.h>
|
|
#include <libdevcore/picosha2.h>
|
|
|
|
using namespace std;
|
|
using namespace dev;
|
|
using namespace dev::test;
|
|
|
|
EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::vm& _vmInstance):
|
|
m_vm(_vmInstance)
|
|
{
|
|
if (_evmVersion == langutil::EVMVersion::homestead())
|
|
m_evmVersion = EVMC_HOMESTEAD;
|
|
else if (_evmVersion == langutil::EVMVersion::tangerineWhistle())
|
|
m_evmVersion = EVMC_TANGERINE_WHISTLE;
|
|
else if (_evmVersion == langutil::EVMVersion::spuriousDragon())
|
|
m_evmVersion = EVMC_SPURIOUS_DRAGON;
|
|
else if (_evmVersion == langutil::EVMVersion::byzantium())
|
|
m_evmVersion = EVMC_BYZANTIUM;
|
|
else if (_evmVersion == langutil::EVMVersion::constantinople())
|
|
m_evmVersion = EVMC_CONSTANTINOPLE;
|
|
else //if (_evmVersion == langutil::EVMVersion::petersburg())
|
|
m_evmVersion = EVMC_PETERSBURG;
|
|
}
|
|
|
|
evmc_storage_status EVMHost::set_storage(const evmc_address& _addr, const evmc_bytes32& _key, const evmc_bytes32& _value) noexcept
|
|
{
|
|
evmc_bytes32 previousValue = m_state.accounts[_addr].storage[_key];
|
|
m_state.accounts[_addr].storage[_key] = _value;
|
|
|
|
// TODO EVMC_STORAGE_MODIFIED_AGAIN should be also used
|
|
if (previousValue == _value)
|
|
return EVMC_STORAGE_UNCHANGED;
|
|
else if (previousValue == evmc_bytes32{})
|
|
return EVMC_STORAGE_ADDED;
|
|
else if (_value == evmc_bytes32{})
|
|
return EVMC_STORAGE_DELETED;
|
|
else
|
|
return EVMC_STORAGE_MODIFIED;
|
|
|
|
}
|
|
|
|
void EVMHost::selfdestruct(const evmc_address& _addr, const evmc_address& _beneficiary) noexcept
|
|
{
|
|
// TODO actual selfdestruct is even more complicated.
|
|
evmc_uint256be balance = m_state.accounts[_addr].balance;
|
|
m_state.accounts.erase(_addr);
|
|
m_state.accounts[_beneficiary].balance = balance;
|
|
}
|
|
|
|
evmc::result EVMHost::call(evmc_message const& _message) noexcept
|
|
{
|
|
if (_message.destination == convertToEVMC(Address(2)))
|
|
return precompileSha256(_message);
|
|
|
|
State stateBackup = m_state;
|
|
|
|
u256 value{convertFromEVMC(_message.value)};
|
|
Account& sender = m_state.accounts[_message.sender];
|
|
|
|
bytes code;
|
|
|
|
evmc_message message = _message;
|
|
if (message.kind == EVMC_CREATE)
|
|
{
|
|
// TODO this is not the right formula
|
|
// TODO is the nonce incremented on failure, too?
|
|
Address createAddress(keccak256(
|
|
bytes(begin(message.sender.bytes), end(message.sender.bytes)) +
|
|
asBytes(to_string(sender.nonce++))
|
|
));
|
|
message.destination = convertToEVMC(createAddress);
|
|
code = bytes(message.input_data, message.input_data + message.input_size);
|
|
}
|
|
else if (message.kind == EVMC_DELEGATECALL)
|
|
{
|
|
code = m_state.accounts[message.destination].code;
|
|
message.destination = m_currentAddress;
|
|
}
|
|
else if (message.kind == EVMC_CALLCODE)
|
|
{
|
|
code = m_state.accounts[message.destination].code;
|
|
message.destination = m_currentAddress;
|
|
}
|
|
else
|
|
code = m_state.accounts[message.destination].code;
|
|
//TODO CREATE2
|
|
|
|
Account& destination = m_state.accounts[message.destination];
|
|
|
|
if (value != 0 && message.kind != EVMC_DELEGATECALL && message.kind != EVMC_CALLCODE)
|
|
{
|
|
sender.balance = convertToEVMC(u256(convertFromEVMC(sender.balance)) - value);
|
|
destination.balance = convertToEVMC(u256(convertFromEVMC(destination.balance)) + value);
|
|
}
|
|
|
|
evmc_address currentAddress = m_currentAddress;
|
|
m_currentAddress = message.destination;
|
|
evmc::result result = m_vm.execute(*this, m_evmVersion, message, code.data(), code.size());
|
|
m_currentAddress = currentAddress;
|
|
|
|
if (result.status_code != EVMC_SUCCESS)
|
|
m_state = stateBackup;
|
|
else if (message.kind == EVMC_CREATE)
|
|
{
|
|
result.create_address = message.destination;
|
|
destination.code = bytes(result.output_data, result.output_data + result.output_size);
|
|
destination.codeHash = convertToEVMC(keccak256(destination.code));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
evmc_tx_context EVMHost::get_tx_context() noexcept
|
|
{
|
|
evmc_tx_context ctx = {};
|
|
ctx.block_timestamp = m_state.timestamp;
|
|
ctx.block_number = m_state.blockNumber;
|
|
ctx.block_coinbase = m_coinbase;
|
|
ctx.block_difficulty = convertToEVMC(u256("200000000"));
|
|
ctx.block_gas_limit = 20000000;
|
|
ctx.tx_gas_price = convertToEVMC(u256("3000000000"));
|
|
ctx.tx_origin = convertToEVMC(Address("0x9292929292929292929292929292929292929292"));
|
|
return ctx;
|
|
}
|
|
|
|
evmc_bytes32 EVMHost::get_block_hash(int64_t _number) noexcept
|
|
{
|
|
return convertToEVMC(u256("0x3737373737373737373737373737373737373737373737373737373737373737") + _number);
|
|
}
|
|
|
|
void EVMHost::emit_log(
|
|
evmc_address const& _addr,
|
|
uint8_t const* _data,
|
|
size_t _dataSize,
|
|
evmc_bytes32 const _topics[],
|
|
size_t _topicsCount
|
|
) noexcept
|
|
{
|
|
LogEntry entry;
|
|
entry.address = convertFromEVMC(_addr);
|
|
for (size_t i = 0; i < _topicsCount; ++i)
|
|
entry.topics.emplace_back(convertFromEVMC(_topics[i]));
|
|
entry.data = bytes(_data, _data + _dataSize);
|
|
m_state.logs.emplace_back(std::move(entry));
|
|
}
|
|
|
|
|
|
Address EVMHost::convertFromEVMC(evmc_address const& _addr)
|
|
{
|
|
return Address(bytes(begin(_addr.bytes), end(_addr.bytes)));
|
|
}
|
|
|
|
evmc_address EVMHost::convertToEVMC(Address const& _addr)
|
|
{
|
|
evmc_address a;
|
|
for (size_t i = 0; i < 20; ++i)
|
|
a.bytes[i] = _addr[i];
|
|
return a;
|
|
}
|
|
|
|
h256 EVMHost::convertFromEVMC(evmc_bytes32 const& _data)
|
|
{
|
|
return h256(bytes(begin(_data.bytes), end(_data.bytes)));
|
|
}
|
|
|
|
evmc_bytes32 EVMHost::convertToEVMC(h256 const& _data)
|
|
{
|
|
evmc_bytes32 d;
|
|
for (size_t i = 0; i < 32; ++i)
|
|
d.bytes[i] = _data[i];
|
|
return d;
|
|
}
|
|
|
|
evmc::result EVMHost::precompileSha256(evmc_message const& _message) noexcept
|
|
{
|
|
// static data so that we do not need a release routine...
|
|
bytes static hash;
|
|
hash = picosha2::hash256(bytes(
|
|
_message.input_data,
|
|
_message.input_data + _message.input_size
|
|
));
|
|
|
|
evmc::result result({});
|
|
result.output_data = hash.data();
|
|
result.output_size = hash.size();
|
|
return result;
|
|
}
|