solidity/test/EVMHost.cpp

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;
}