Merge pull request #13532 from ethereum/evmc-update

Upgrade to evmc10 + evmone9
This commit is contained in:
Nikola Matić 2022-11-10 02:10:29 -05:00 committed by GitHub
commit 2fb7205dd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 977 additions and 351 deletions

View File

@ -9,16 +9,16 @@ version: 2.1
parameters:
ubuntu-2004-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-14
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d1ef23849db4c5462b248d89c111da4009b153cbd5002cb8755b0580312be581"
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-15
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:e4f83457bf1d6475c3189e9013da77289793a5ecd6a0e15dbec9411880b11e22"
ubuntu-2004-clang-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-14
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:beb8c91998ec0df99a488900b3723a06f1122f0954fc73786b6c53fd73a6408d"
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-15
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:8dda4fdae312f840fbb4e25b9ef01ad3209e9014e49e4564ab0f0d2510225131"
ubuntu-1604-clang-ossfuzz-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-19
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:8c9bf1813c261d781f4c65fceed2dfb3ecf5be9ecf49bddbd250b570a7f3baea"
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-20
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:7b3ccaed3b5d37dc2cc4bbe1a1e40949266292dcdbc3ad015271ab4e5e6f5038"
emscripten-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:emscripten-13

View File

@ -74,18 +74,18 @@ then
rm -r "$z3_dir"
# evmone
evmone_version="0.8.0"
evmone_version="0.9.1"
evmone_package="evmone-${evmone_version}-darwin-x86_64.tar.gz"
wget "https://github.com/ethereum/evmone/releases/download/v${evmone_version}/${evmone_package}"
validate_checksum "$evmone_package" e8efef478822f0ed6d0493e89004181e895893f93963152a2a81589acc3a0828
validate_checksum "$evmone_package" 70420a893a9b1036fcb63526b806d97658db8c373bcab1c3e8382594dc8593e4
tar xzpf "$evmone_package" -C /usr/local
rm "$evmone_package"
# hera
hera_version="0.5.0"
hera_version="0.6.0"
hera_package="hera-${hera_version}-darwin-x86_64.tar.gz"
wget "https://github.com/ewasm/hera/releases/download/v${hera_version}/${hera_package}"
validate_checksum "$hera_package" 190050d7ace384ecd79ec1b1f607a9ff40e196b4eec75932958d4814d221d059
validate_checksum "$hera_package" 82ee57404862705ab314f7a4d04bf2cf29d71e8d209850d66c125527cd287f37
tar xzpf "$hera_package" -C /usr/local
rm "$hera_package"
fi

View File

@ -3,7 +3,7 @@ $ErrorActionPreference = "Stop"
# Needed for Invoke-WebRequest to work via CI.
$progressPreference = "silentlyContinue"
Invoke-WebRequest -URI "https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-windows-amd64.zip" -OutFile "evmone.zip"
Invoke-WebRequest -URI "https://github.com/ethereum/evmone/releases/download/v0.9.1/evmone-0.9.1-windows-amd64.zip" -OutFile "evmone.zip"
tar -xf evmone.zip "bin/evmone.dll"
mkdir deps
mv bin/evmone.dll deps

View File

@ -33,19 +33,19 @@ namespace solidity::test
#ifdef _WIN32
static constexpr auto evmoneFilename = "evmone.dll";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-windows-amd64.zip";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.9.1/evmone-0.9.1-windows-amd64.zip";
static constexpr auto heraFilename = "hera.dll";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/archive/v0.3.2-evmc8.tar.gz";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/archive/v0.6.0.tar.gz";
#elif defined(__APPLE__)
static constexpr auto evmoneFilename = "libevmone.dylib";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-darwin-x86_64.tar.gz";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.9.1/evmone-0.9.1-darwin-x86_64.tar.gz";
static constexpr auto heraFilename = "libhera.dylib";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.5.0/hera-0.5.0-darwin-x86_64.tar.gz";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.6.0/hera-0.6.0-darwin-x86_64.tar.gz";
#else
static constexpr auto evmoneFilename = "libevmone.so";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-linux-x86_64.tar.gz";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.9.1/evmone-0.9.1-linux-x86_64.tar.gz";
static constexpr auto heraFilename = "libhera.so";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.5.0/hera-0.5.0-linux-x86_64.tar.gz";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.6.0/hera-0.6.0-linux-x86_64.tar.gz";
#endif
struct ConfigException: public util::Exception {};

View File

@ -122,10 +122,15 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm):
m_evmRevision = EVMC_BERLIN;
else if (_evmVersion == langutil::EVMVersion::london())
m_evmRevision = EVMC_LONDON;
// TODO: support EVMVersion::paris()
else
assertThrow(false, Exception, "Unsupported EVM version");
tx_context.block_difficulty = evmc::uint256be{200000000};
if (m_evmRevision >= EVMC_PARIS)
// This is the value from the merge block.
tx_context.block_prev_randao = 0xa86c2e601b6c44eb4848f7d23d9df3113fbcac42041c49cbed5000cb4f118777_bytes32;
else
tx_context.block_prev_randao = evmc::uint256be{200000000};
tx_context.block_gas_limit = 20000000;
tx_context.block_coinbase = 0x7878787878787878787878787878787878787878_address;
tx_context.tx_gas_price = evmc::uint256be{3000000000};
@ -145,7 +150,6 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm):
void EVMHost::reset()
{
accounts.clear();
m_currentAddress = {};
// Clear self destruct records
recorded_selfdestructs.clear();
// Clear call records
@ -194,14 +198,14 @@ void EVMHost::transfer(evmc::MockedAccount& _sender, evmc::MockedAccount& _recip
_recipient.balance = convertToEVMC(u256(convertFromEVMC(_recipient.balance)) + _value);
}
void EVMHost::selfdestruct(const evmc::address& _addr, const evmc::address& _beneficiary) noexcept
bool EVMHost::selfdestruct(const evmc::address& _addr, const evmc::address& _beneficiary) noexcept
{
// TODO actual selfdestruct is even more complicated.
transfer(accounts[_addr], accounts[_beneficiary], convertFromEVMC(accounts[_addr].balance));
// Record self destructs. Clearing will be done in newTransactionFrame().
MockedHost::selfdestruct(_addr, _beneficiary);
return MockedHost::selfdestruct(_addr, _beneficiary);
}
void EVMHost::recordCalls(evmc_message const& _message) noexcept
@ -212,34 +216,34 @@ void EVMHost::recordCalls(evmc_message const& _message) noexcept
// NOTE: this is used for both internal and external calls.
// External calls are triggered from ExecutionFramework and contain only EVMC_CREATE or EVMC_CALL.
evmc::result EVMHost::call(evmc_message const& _message) noexcept
evmc::Result EVMHost::call(evmc_message const& _message) noexcept
{
recordCalls(_message);
if (_message.destination == 0x0000000000000000000000000000000000000001_address)
if (_message.recipient == 0x0000000000000000000000000000000000000001_address)
return precompileECRecover(_message);
else if (_message.destination == 0x0000000000000000000000000000000000000002_address)
else if (_message.recipient == 0x0000000000000000000000000000000000000002_address)
return precompileSha256(_message);
else if (_message.destination == 0x0000000000000000000000000000000000000003_address)
else if (_message.recipient == 0x0000000000000000000000000000000000000003_address)
return precompileRipeMD160(_message);
else if (_message.destination == 0x0000000000000000000000000000000000000004_address)
else if (_message.recipient == 0x0000000000000000000000000000000000000004_address)
return precompileIdentity(_message);
else if (_message.destination == 0x0000000000000000000000000000000000000005_address && m_evmVersion >= langutil::EVMVersion::byzantium())
else if (_message.recipient == 0x0000000000000000000000000000000000000005_address && m_evmVersion >= langutil::EVMVersion::byzantium())
return precompileModExp(_message);
else if (_message.destination == 0x0000000000000000000000000000000000000006_address && m_evmVersion >= langutil::EVMVersion::byzantium())
else if (_message.recipient == 0x0000000000000000000000000000000000000006_address && m_evmVersion >= langutil::EVMVersion::byzantium())
{
if (m_evmVersion <= langutil::EVMVersion::istanbul())
return precompileALTBN128G1Add<EVMC_ISTANBUL>(_message);
else
return precompileALTBN128G1Add<EVMC_LONDON>(_message);
}
else if (_message.destination == 0x0000000000000000000000000000000000000007_address && m_evmVersion >= langutil::EVMVersion::byzantium())
else if (_message.recipient == 0x0000000000000000000000000000000000000007_address && m_evmVersion >= langutil::EVMVersion::byzantium())
{
if (m_evmVersion <= langutil::EVMVersion::istanbul())
return precompileALTBN128G1Mul<EVMC_ISTANBUL>(_message);
else
return precompileALTBN128G1Mul<EVMC_LONDON>(_message);
}
else if (_message.destination == 0x0000000000000000000000000000000000000008_address && m_evmVersion >= langutil::EVMVersion::byzantium())
else if (_message.recipient == 0x0000000000000000000000000000000000000008_address && m_evmVersion >= langutil::EVMVersion::byzantium())
{
if (m_evmVersion <= langutil::EVMVersion::istanbul())
return precompileALTBN128PairingProduct<EVMC_ISTANBUL>(_message);
@ -262,7 +266,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept
message.gas -= message.input_data[i] == 0 ? evmasm::GasCosts::txDataZeroGas : evmasm::GasCosts::txDataNonZeroGas(m_evmVersion);
if (message.gas < 0)
{
evmc::result result({});
evmc::Result result;
result.status_code = EVMC_OUT_OF_GAS;
accounts = stateBackup;
return result;
@ -299,8 +303,8 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept
encodedNonce
), h160::AlignRight);
message.destination = convertToEVMC(createAddress);
assertThrow(accounts.count(message.destination) == 0, Exception, "Account cannot exist");
message.recipient = convertToEVMC(createAddress);
assertThrow(accounts.count(message.recipient) == 0, Exception, "Account cannot exist");
code = evmc::bytes(message.input_data, message.input_data + message.input_size);
}
@ -313,13 +317,13 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept
keccak256(bytes(message.input_data, message.input_data + message.input_size)).asBytes()
), h160::AlignRight);
message.destination = convertToEVMC(createAddress);
if (accounts.count(message.destination) && (
accounts[message.destination].nonce > 0 ||
!accounts[message.destination].code.empty()
message.recipient = convertToEVMC(createAddress);
if (accounts.count(message.recipient) && (
accounts[message.recipient].nonce > 0 ||
!accounts[message.recipient].code.empty()
))
{
evmc::result result({});
evmc::Result result;
result.status_code = EVMC_OUT_OF_GAS;
accounts = stateBackup;
return result;
@ -327,21 +331,16 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept
code = evmc::bytes(message.input_data, message.input_data + message.input_size);
}
else if (message.kind == EVMC_DELEGATECALL || message.kind == EVMC_CALLCODE)
{
code = accounts[message.destination].code;
message.destination = m_currentAddress;
}
else
code = accounts[message.destination].code;
code = accounts[message.code_address].code;
auto& destination = accounts[message.destination];
auto& destination = accounts[message.recipient];
if (value != 0 && message.kind != EVMC_DELEGATECALL && message.kind != EVMC_CALLCODE)
{
if (value > convertFromEVMC(sender.balance))
{
evmc::result result({});
evmc::Result result;
result.status_code = EVMC_INSUFFICIENT_BALANCE;
accounts = stateBackup;
return result;
@ -355,12 +354,9 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept
if (m_evmRevision >= EVMC_BERLIN)
{
access_account(message.sender);
access_account(message.destination);
access_account(message.recipient);
}
evmc::address currentAddress = m_currentAddress;
m_currentAddress = message.destination;
evmc::result result = m_vm.execute(*this, m_evmRevision, message, code.data(), code.size());
m_currentAddress = currentAddress;
evmc::Result result = m_vm.execute(*this, m_evmRevision, message, code.data(), code.size());
if (message.kind == EVMC_CREATE || message.kind == EVMC_CREATE2)
{
@ -373,7 +369,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept
}
else
{
result.create_address = message.destination;
result.create_address = message.recipient;
destination.code = evmc::bytes(result.output_data, result.output_data + result.output_size);
destination.codehash = convertToEVMC(keccak256({result.output_data, result.output_size}));
}
@ -416,7 +412,7 @@ evmc::bytes32 EVMHost::convertToEVMC(h256 const& _data)
return d;
}
evmc::result EVMHost::precompileECRecover(evmc_message const& _message) noexcept
evmc::Result EVMHost::precompileECRecover(evmc_message const& _message) noexcept
{
// NOTE this is a partial implementation for some inputs.
@ -449,20 +445,14 @@ evmc::result EVMHost::precompileECRecover(evmc_message const& _message) noexcept
}
}
};
evmc::result result = precompileGeneric(_message, inputOutput);
evmc::Result result = precompileGeneric(_message, inputOutput);
// ECRecover will return success with empty response in case of failure
if (result.status_code != EVMC_SUCCESS && result.status_code != EVMC_OUT_OF_GAS)
// return resultWithGas(_message.gas, gas_cost, {});
{
result.status_code = EVMC_SUCCESS;
result.gas_left = _message.gas - gas_cost;
result.output_data = {};
result.output_size = 0;
}
return resultWithGas(_message.gas, gas_cost, {});
return result;
}
evmc::result EVMHost::precompileSha256(evmc_message const& _message) noexcept
evmc::Result EVMHost::precompileSha256(evmc_message const& _message) noexcept
{
// static data so that we do not need a release routine...
bytes static hash;
@ -477,7 +467,7 @@ evmc::result EVMHost::precompileSha256(evmc_message const& _message) noexcept
return resultWithGas(_message.gas, gas_cost, hash);
}
evmc::result EVMHost::precompileRipeMD160(evmc_message const& _message) noexcept
evmc::Result EVMHost::precompileRipeMD160(evmc_message const& _message) noexcept
{
// NOTE this is a partial implementation for some inputs.
@ -579,7 +569,7 @@ evmc::result EVMHost::precompileRipeMD160(evmc_message const& _message) noexcept
return precompileGeneric(_message, inputOutput);
}
evmc::result EVMHost::precompileIdentity(evmc_message const& _message) noexcept
evmc::Result EVMHost::precompileIdentity(evmc_message const& _message) noexcept
{
// static data so that we do not need a release routine...
bytes static data;
@ -591,14 +581,14 @@ evmc::result EVMHost::precompileIdentity(evmc_message const& _message) noexcept
return resultWithGas(_message.gas, gas_cost, data);
}
evmc::result EVMHost::precompileModExp(evmc_message const&) noexcept
evmc::Result EVMHost::precompileModExp(evmc_message const&) noexcept
{
// TODO implement
return resultWithFailure();
}
template <evmc_revision Revision>
evmc::result EVMHost::precompileALTBN128G1Add(evmc_message const& _message) noexcept
evmc::Result EVMHost::precompileALTBN128G1Add(evmc_message const& _message) noexcept
{
// NOTE this is a partial implementation for some inputs.
@ -866,7 +856,7 @@ evmc::result EVMHost::precompileALTBN128G1Add(evmc_message const& _message) noex
}
template <evmc_revision Revision>
evmc::result EVMHost::precompileALTBN128G1Mul(evmc_message const& _message) noexcept
evmc::Result EVMHost::precompileALTBN128G1Mul(evmc_message const& _message) noexcept
{
// NOTE this is a partial implementation for some inputs.
@ -956,7 +946,7 @@ evmc::result EVMHost::precompileALTBN128G1Mul(evmc_message const& _message) noex
}
template <evmc_revision Revision>
evmc::result EVMHost::precompileALTBN128PairingProduct(evmc_message const& _message) noexcept
evmc::Result EVMHost::precompileALTBN128PairingProduct(evmc_message const& _message) noexcept
{
// Base + per pairing gas.
constexpr auto calc_cost = [](unsigned points) -> int64_t {
@ -1124,7 +1114,7 @@ evmc::result EVMHost::precompileALTBN128PairingProduct(evmc_message const& _mess
return precompileGeneric(_message, inputOutput);
}
evmc::result EVMHost::precompileGeneric(
evmc::Result EVMHost::precompileGeneric(
evmc_message const& _message,
map<bytes, EVMPrecompileOutput> const& _inOut) noexcept
{
@ -1138,20 +1128,20 @@ evmc::result EVMHost::precompileGeneric(
return resultWithFailure();
}
evmc::result EVMHost::resultWithFailure() noexcept
evmc::Result EVMHost::resultWithFailure() noexcept
{
evmc::result result({});
evmc::Result result;
result.status_code = EVMC_FAILURE;
return result;
}
evmc::result EVMHost::resultWithGas(
evmc::Result EVMHost::resultWithGas(
int64_t gas_limit,
int64_t gas_required,
bytes const& _data
) noexcept
{
evmc::result result({});
evmc::Result result;
if (gas_limit < gas_required)
{
result.status_code = EVMC_OUT_OF_GAS;
@ -1162,7 +1152,7 @@ evmc::result EVMHost::resultWithGas(
result.status_code = EVMC_SUCCESS;
result.gas_left = gas_limit - gas_required;
}
result.output_data = _data.data();
result.output_data = _data.empty() ? nullptr : _data.data();
result.output_size = _data.size();
return result;
}
@ -1209,10 +1199,11 @@ void EVMHostPrinter::balance()
void EVMHostPrinter::selfdestructRecords()
{
for (auto const& record: m_host.recorded_selfdestructs)
m_stateStream << "SELFDESTRUCT"
<< " BENEFICIARY "
<< m_host.convertFromEVMC(record.beneficiary)
<< endl;
for (auto const& beneficiary: record.second)
m_stateStream << "SELFDESTRUCT"
<< " BENEFICIARY "
<< m_host.convertFromEVMC(beneficiary)
<< endl;
}
void EVMHostPrinter::callRecords()

View File

@ -35,7 +35,7 @@
namespace solidity::test
{
using Address = util::h160;
using StorageMap = std::map<evmc::bytes32, evmc::storage_value>;
using StorageMap = std::map<evmc::bytes32, evmc::StorageValue>;
struct EVMPrecompileOutput {
bytes const output;
@ -59,8 +59,8 @@ public:
using MockedHost::access_storage;
// Modified features of MockedHost.
void selfdestruct(evmc::address const& _addr, evmc::address const& _beneficiary) noexcept final;
evmc::result call(evmc_message const& _message) noexcept final;
bool selfdestruct(evmc::address const& _addr, evmc::address const& _beneficiary) noexcept final;
evmc::Result call(evmc_message const& _message) noexcept final;
evmc::bytes32 get_block_hash(int64_t number) const noexcept final;
// Solidity testing specific features.
@ -98,35 +98,34 @@ public:
static util::h256 convertFromEVMC(evmc::bytes32 const& _data);
static evmc::bytes32 convertToEVMC(util::h256 const& _data);
private:
evmc::address m_currentAddress = {};
/// Transfer value between accounts. Checks for sufficient balance.
void transfer(evmc::MockedAccount& _sender, evmc::MockedAccount& _recipient, u256 const& _value) noexcept;
/// Start a new transaction frame.
/// This will perform selfdestructs and clear account/storage access indicator for EIP-2929.
/// This will perform selfdestructs, apply storage status changes across all accounts,
/// and clear account/storage access indicator for EIP-2929.
void newTransactionFrame();
/// Records calls made via @param _message.
void recordCalls(evmc_message const& _message) noexcept;
static evmc::result precompileECRecover(evmc_message const& _message) noexcept;
static evmc::result precompileSha256(evmc_message const& _message) noexcept;
static evmc::result precompileRipeMD160(evmc_message const& _message) noexcept;
static evmc::result precompileIdentity(evmc_message const& _message) noexcept;
static evmc::result precompileModExp(evmc_message const& _message) noexcept;
static evmc::Result precompileECRecover(evmc_message const& _message) noexcept;
static evmc::Result precompileSha256(evmc_message const& _message) noexcept;
static evmc::Result precompileRipeMD160(evmc_message const& _message) noexcept;
static evmc::Result precompileIdentity(evmc_message const& _message) noexcept;
static evmc::Result precompileModExp(evmc_message const& _message) noexcept;
template <evmc_revision Revision>
static evmc::result precompileALTBN128G1Add(evmc_message const& _message) noexcept;
static evmc::Result precompileALTBN128G1Add(evmc_message const& _message) noexcept;
template <evmc_revision Revision>
static evmc::result precompileALTBN128G1Mul(evmc_message const& _message) noexcept;
static evmc::Result precompileALTBN128G1Mul(evmc_message const& _message) noexcept;
template <evmc_revision Revision>
static evmc::result precompileALTBN128PairingProduct(evmc_message const& _message) noexcept;
static evmc::result precompileGeneric(evmc_message const& _message, std::map<bytes, EVMPrecompileOutput> const& _inOut) noexcept;
static evmc::Result precompileALTBN128PairingProduct(evmc_message const& _message) noexcept;
static evmc::Result precompileGeneric(evmc_message const& _message, std::map<bytes, EVMPrecompileOutput> const& _inOut) noexcept;
/// @returns a result object with gas usage and result data taken from @a _data.
/// The outcome will be a failure if the limit < required.
/// @note The return value is only valid as long as @a _data is alive!
static evmc::result resultWithGas(int64_t gas_limit, int64_t gas_required, bytes const& _data) noexcept;
static evmc::result resultWithFailure() noexcept;
static evmc::Result resultWithGas(int64_t gas_limit, int64_t gas_required, bytes const& _data) noexcept;
static evmc::Result resultWithFailure() noexcept;
evmc::VM& m_vm;
/// EVM version requested by the testing tool

View File

@ -165,7 +165,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256
cout << " value: " << _value << endl;
cout << " in: " << util::toHex(_data) << endl;
}
evmc_message message = {};
evmc_message message{};
message.input_data = _data.data();
message.input_size = _data.size();
message.sender = EVMHost::convertToEVMC(m_sender);
@ -174,16 +174,19 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256
if (_isCreation)
{
message.kind = EVMC_CREATE;
message.destination = EVMHost::convertToEVMC(h160{});
message.recipient = {};
message.code_address = {};
}
else
{
message.kind = EVMC_CALL;
message.destination = EVMHost::convertToEVMC(m_contractAddress);
message.recipient = EVMHost::convertToEVMC(m_contractAddress);
message.code_address = message.recipient;
}
message.gas = InitialGas.convert_to<int64_t>();
evmc::result result = m_evmcHost->call(message);
evmc::Result result = m_evmcHost->call(message);
m_output = bytes(result.output_data, result.output_data + result.output_size);
if (_isCreation)
@ -210,11 +213,12 @@ void ExecutionFramework::sendEther(h160 const& _addr, u256 const& _amount)
if (_amount > 0)
cout << " value: " << _amount << endl;
}
evmc_message message = {};
evmc_message message{};
message.sender = EVMHost::convertToEVMC(m_sender);
message.value = EVMHost::convertToEVMC(_amount);
message.kind = EVMC_CALL;
message.destination = EVMHost::convertToEVMC(_addr);
message.recipient = EVMHost::convertToEVMC(_addr);
message.code_address = message.recipient;
message.gas = InitialGas.convert_to<int64_t>();
m_evmcHost->call(message);

View File

@ -272,7 +272,6 @@ private:
}
protected:
u256 const GasPrice = 10 * gwei;
u256 const InitialGas = 100000000;
void selectVM(evmc_capabilities _cap = evmc_capabilities::EVMC_CAPABILITY_EVM1);

View File

@ -1,5 +1,5 @@
# EVMC
This is an import of [EVMC](https://github.com/ethereum/evmc) version [9.0.0](https://github.com/ethereum/evmc/releases/tag/v9.0.0).
This is an import of [EVMC](https://github.com/ethereum/evmc) version [10.0.0](https://github.com/ethereum/evmc/releases/tag/v10.0.0).
Important: The `MockedAccount.storage` is changed to a map from unordered_map as ordering is important for fuzzing.

View File

@ -2,7 +2,7 @@
* EVMC: Ethereum Client-VM Connector API
*
* @copyright
* Copyright 2016-2019 The EVMC Authors.
* Copyright 2016 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*
* @defgroup EVMC EVMC
@ -44,7 +44,7 @@ enum
*
* @see @ref versioning
*/
EVMC_ABI_VERSION = 9
EVMC_ABI_VERSION = 10
};
@ -89,8 +89,9 @@ enum evmc_flags
};
/**
* The message describing an EVM call,
* including a zero-depth calls from a transaction origin.
* The message describing an EVM call, including a zero-depth calls from a transaction origin.
*
* Most of the fields are modelled by the section 8. Message Call of the Ethereum Yellow Paper.
*/
struct evmc_message
{
@ -103,21 +104,48 @@ struct evmc_message
*/
uint32_t flags;
/** The call depth. */
/**
* The present depth of the message call stack.
*
* Defined as `e` in the Yellow Paper.
*/
int32_t depth;
/** The amount of gas for message execution. */
/**
* The amount of gas available to the message execution.
*
* Defined as `g` in the Yellow Paper.
*/
int64_t gas;
/** The destination of the message. */
evmc_address destination;
/**
* The recipient of the message.
*
* This is the address of the account which storage/balance/nonce is going to be modified
* by the message execution. In case of ::EVMC_CALL, this is also the account where the
* message value evmc_message::value is going to be transferred.
* For ::EVMC_CALLCODE or ::EVMC_DELEGATECALL, this may be different from
* the evmc_message::code_address.
*
* Defined as `r` in the Yellow Paper.
*/
evmc_address recipient;
/** The sender of the message. */
/**
* The sender of the message.
*
* The address of the sender of a message call defined as `s` in the Yellow Paper.
* This must be the message recipient of the message at the previous (lower) depth,
* except for the ::EVMC_DELEGATECALL where recipient is the 2 levels above the present depth.
* At the depth 0 this must be the transaction origin.
*/
evmc_address sender;
/**
* The message input data.
*
* The arbitrary length byte array of the input data of the call,
* defined as `d` in the Yellow Paper.
* This MAY be NULL.
*/
const uint8_t* input_data;
@ -131,30 +159,49 @@ struct evmc_message
/**
* The amount of Ether transferred with the message.
*
* This is transferred value for ::EVMC_CALL or apparent value for ::EVMC_DELEGATECALL.
* Defined as `v` or `v~` in the Yellow Paper.
*/
evmc_uint256be value;
/**
* The optional value used in new contract address construction.
*
* Ignored unless kind is EVMC_CREATE2.
* Needed only for a Host to calculate created address when kind is ::EVMC_CREATE2.
* Ignored in evmc_execute_fn().
*/
evmc_bytes32 create2_salt;
/**
* The address of the code to be executed.
*
* For ::EVMC_CALLCODE or ::EVMC_DELEGATECALL this may be different from
* the evmc_message::recipient.
* Not required when invoking evmc_execute_fn(), only when invoking evmc_call_fn().
* Ignored if kind is ::EVMC_CREATE or ::EVMC_CREATE2.
*
* In case of ::EVMC_CAPABILITY_PRECOMPILES implementation, this fields should be inspected
* to identify the requested precompile.
*
* Defined as `c` in the Yellow Paper.
*/
evmc_address code_address;
};
/** The transaction and block data for execution. */
struct evmc_tx_context
{
evmc_uint256be tx_gas_price; /**< The transaction gas price. */
evmc_address tx_origin; /**< The transaction origin account. */
evmc_address block_coinbase; /**< The miner of the block. */
int64_t block_number; /**< The block number. */
int64_t block_timestamp; /**< The block timestamp. */
int64_t block_gas_limit; /**< The block gas limit. */
evmc_uint256be block_difficulty; /**< The block difficulty. */
evmc_uint256be chain_id; /**< The blockchain's ChainID. */
evmc_uint256be block_base_fee; /**< The block base fee per gas (EIP-1559, EIP-3198). */
evmc_uint256be tx_gas_price; /**< The transaction gas price. */
evmc_address tx_origin; /**< The transaction origin account. */
evmc_address block_coinbase; /**< The miner of the block. */
int64_t block_number; /**< The block number. */
int64_t block_timestamp; /**< The block timestamp. */
int64_t block_gas_limit; /**< The block gas limit. */
evmc_uint256be block_prev_randao; /**< The block previous RANDAO (EIP-4399). */
evmc_uint256be chain_id; /**< The blockchain's ChainID. */
evmc_uint256be block_base_fee; /**< The block base fee per gas (EIP-1559, EIP-3198). */
};
/**
@ -354,6 +401,14 @@ struct evmc_result
*/
int64_t gas_left;
/**
* The refunded gas accumulated from this execution and its sub-calls.
*
* The transaction gas refund limit is not applied.
* If evmc_result::status_code is other than ::EVMC_SUCCESS the value MUST be 0.
*/
int64_t gas_refund;
/**
* The reference to output data.
*
@ -396,12 +451,11 @@ struct evmc_result
evmc_release_result_fn release;
/**
* The address of the contract created by create instructions.
* The address of the possibly created contract.
*
* This field has valid value only if:
* - it is a result of the Host method evmc_host_interface::call
* - and the result describes successful contract creation
* (evmc_result::status_code is ::EVMC_SUCCESS).
* The create address may be provided even though the contract creation has failed
* (evmc_result::status_code is not ::EVMC_SUCCESS). This is useful in situations
* when the address is observable, e.g. access to it remains warm.
* In all other cases the address MUST be null bytes.
*/
evmc_address create_address;
@ -452,40 +506,97 @@ typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_host_context* context,
/**
* The effect of an attempt to modify a contract storage item.
*
* See @ref storagestatus for additional information about design of this enum
* and analysis of the specification.
*
* For the purpose of explaining the meaning of each element, the following
* notation is used:
* - 0 is zero value,
* - X != 0 (X is any value other than 0),
* - Y != X, Y != 0 (Y is any value other than X and 0),
* - Z != Y (Z is any value other than Y),
* - the "->" means the change from one value to another.
* - Y != 0, Y != X, (Y is any value other than X and 0),
* - Z != 0, Z != X, Z != X (Z is any value other than Y and X and 0),
* - the "o -> c -> v" triple describes the change status in the context of:
* - o: original value (cold value before a transaction started),
* - c: current storage value,
* - v: new storage value to be set.
*
* The order of elements follows EIPs introducing net storage gas costs:
* - EIP-2200: https://eips.ethereum.org/EIPS/eip-2200,
* - EIP-1283: https://eips.ethereum.org/EIPS/eip-1283.
*/
enum evmc_storage_status
{
/**
* The value of a storage item has been left unchanged: 0 -> 0 and X -> X.
* The new/same value is assigned to the storage item without affecting the cost structure.
*
* The storage value item is either:
* - left unchanged (c == v) or
* - the dirty value (o != c) is modified again (c != v).
* This is the group of cases related to minimal gas cost of only accessing warm storage.
* 0|X -> 0 -> 0 (current value unchanged)
* 0|X|Y -> Y -> Y (current value unchanged)
* 0|X -> Y -> Z (modified previously added/modified value)
*
* This is "catch all remaining" status. I.e. if all other statuses are correctly matched
* this status should be assigned to all remaining cases.
*/
EVMC_STORAGE_UNCHANGED = 0,
EVMC_STORAGE_ASSIGNED = 0,
/**
* The value of a storage item has been modified: X -> Y.
* A new storage item is added by changing
* the current clean zero to a nonzero value.
* 0 -> 0 -> Z
*/
EVMC_STORAGE_MODIFIED = 1,
EVMC_STORAGE_ADDED = 1,
/**
* A storage item has been modified after being modified before: X -> Y -> Z.
* A storage item is deleted by changing
* the current clean nonzero to the zero value.
* X -> X -> 0
*/
EVMC_STORAGE_MODIFIED_AGAIN = 2,
EVMC_STORAGE_DELETED = 2,
/**
* A new storage item has been added: 0 -> X.
* A storage item is modified by changing
* the current clean nonzero to other nonzero value.
* X -> X -> Z
*/
EVMC_STORAGE_ADDED = 3,
EVMC_STORAGE_MODIFIED = 3,
/**
* A storage item has been deleted: X -> 0.
* A storage item is added by changing
* the current dirty zero to a nonzero value other than the original value.
* X -> 0 -> Z
*/
EVMC_STORAGE_DELETED = 4
EVMC_STORAGE_DELETED_ADDED = 4,
/**
* A storage item is deleted by changing
* the current dirty nonzero to the zero value and the original value is not zero.
* X -> Y -> 0
*/
EVMC_STORAGE_MODIFIED_DELETED = 5,
/**
* A storage item is added by changing
* the current dirty zero to the original value.
* X -> 0 -> X
*/
EVMC_STORAGE_DELETED_RESTORED = 6,
/**
* A storage item is deleted by changing
* the current dirty nonzero to the original zero value.
* 0 -> Y -> 0
*/
EVMC_STORAGE_ADDED_DELETED = 7,
/**
* A storage item is modified by changing
* the current dirty nonzero to the original nonzero value other than the current value.
* X -> Y -> X
*/
EVMC_STORAGE_MODIFIED_RESTORED = 8
};
@ -495,7 +606,7 @@ enum evmc_storage_status
* This callback function is used by a VM to update the given account storage entry.
* The VM MUST make sure that the account exists. This requirement is only a formality because
* VM implementations only modify storage of the account of the current execution context
* (i.e. referenced by evmc_message::destination).
* (i.e. referenced by evmc_message::recipient).
*
* @param context The pointer to the Host execution context.
* @param address The address of the account.
@ -579,8 +690,10 @@ typedef size_t (*evmc_copy_code_fn)(struct evmc_host_context* context,
* @param context The pointer to the Host execution context. See ::evmc_host_context.
* @param address The address of the contract to be selfdestructed.
* @param beneficiary The address where the remaining ETH is going to be transferred.
* @return The information if the given address has not been registered as
* selfdestructed yet. True if registered for the first time, false otherwise.
*/
typedef void (*evmc_selfdestruct_fn)(struct evmc_host_context* context,
typedef bool (*evmc_selfdestruct_fn)(struct evmc_host_context* context,
const evmc_address* address,
const evmc_address* beneficiary);
@ -821,26 +934,40 @@ enum evmc_revision
/**
* The Berlin revision.
*
* https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md
* https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md
*/
EVMC_BERLIN = 8,
/**
* The London revision.
*
* https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/london.md
* https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/london.md
*/
EVMC_LONDON = 9,
/**
* The Paris revision (aka The Merge).
*
* https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md
*/
EVMC_PARIS = 10,
/**
* The Shanghai revision.
*
* https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md
* https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md
*/
EVMC_SHANGHAI = 10,
EVMC_SHANGHAI = 11,
/**
* The Cancun revision.
*
* The future next revision after Shanghai.
*/
EVMC_CANCUN = 12,
/** The maximum EVM revision supported. */
EVMC_MAX_REVISION = EVMC_SHANGHAI,
EVMC_MAX_REVISION = EVMC_CANCUN,
/**
* The latest known EVM revision with finalized specification.
@ -894,7 +1021,7 @@ enum evmc_capabilities
/**
* The VM is capable of executing the precompiled contracts
* defined for the range of destination addresses.
* defined for the range of code addresses.
*
* The EIP-1352 (https://eips.ethereum.org/EIPS/eip-1352) specifies
* the range 0x000...0000 - 0x000...ffff of addresses

View File

@ -1,20 +1,28 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018-2020 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
#pragma once
#include <evmc/evmc.h>
#include <evmc/helpers.h>
#include <evmc/hex.hpp>
#include <functional>
#include <initializer_list>
#include <ostream>
#include <string_view>
#include <utility>
static_assert(EVMC_LATEST_STABLE_REVISION <= EVMC_MAX_REVISION,
"latest stable revision ill-defined");
/// EVMC C++ API - wrappers and bindings for C++
/// @ingroup cpp
namespace evmc
{
/// String view of uint8_t chars.
using bytes_view = std::basic_string_view<uint8_t>;
/// The big-endian 160-bit hash suitable for keeping an Ethereum address.
///
/// This type wraps C ::evmc_address to make sure objects of this type are always initialized.
@ -54,6 +62,9 @@ struct address : evmc_address
/// Explicit operator converting to bool.
inline constexpr explicit operator bool() const noexcept;
/// Implicit operator converting to bytes_view.
inline constexpr operator bytes_view() const noexcept { return {bytes, sizeof(bytes)}; }
};
/// The fixed size array of 32 bytes for storing 256-bit EVM values.
@ -106,7 +117,10 @@ struct bytes32 : evmc_bytes32
{}
/// Explicit operator converting to bool.
constexpr inline explicit operator bool() const noexcept;
inline constexpr explicit operator bool() const noexcept;
/// Implicit operator converting to bytes_view.
inline constexpr operator bytes_view() const noexcept { return {bytes, sizeof(bytes)}; }
};
/// The alias for evmc::bytes32 to represent a big-endian 256-bit integer.
@ -267,75 +281,46 @@ inline constexpr bytes32::operator bool() const noexcept
namespace literals
{
namespace internal
{
constexpr int from_hex(char c) noexcept
{
return (c >= 'a' && c <= 'f') ? c - ('a' - 10) :
(c >= 'A' && c <= 'F') ? c - ('A' - 10) : c - '0';
}
constexpr uint8_t byte(const char* s, size_t i) noexcept
{
return static_cast<uint8_t>((from_hex(s[2 * i]) << 4) | from_hex(s[2 * i + 1]));
}
/// Converts a raw literal into value of type T.
///
/// This function is expected to be used on literals in constexpr context only.
/// In case the input is invalid the std::terminate() is called.
/// TODO(c++20): Use consteval.
template <typename T>
T from_hex(const char*) noexcept;
template <>
constexpr bytes32 from_hex<bytes32>(const char* s) noexcept
constexpr T parse(std::string_view s) noexcept
{
return {
{{byte(s, 0), byte(s, 1), byte(s, 2), byte(s, 3), byte(s, 4), byte(s, 5), byte(s, 6),
byte(s, 7), byte(s, 8), byte(s, 9), byte(s, 10), byte(s, 11), byte(s, 12), byte(s, 13),
byte(s, 14), byte(s, 15), byte(s, 16), byte(s, 17), byte(s, 18), byte(s, 19), byte(s, 20),
byte(s, 21), byte(s, 22), byte(s, 23), byte(s, 24), byte(s, 25), byte(s, 26), byte(s, 27),
byte(s, 28), byte(s, 29), byte(s, 30), byte(s, 31)}}};
return from_hex<T>(s).value();
}
template <>
constexpr address from_hex<address>(const char* s) noexcept
{
return {
{{byte(s, 0), byte(s, 1), byte(s, 2), byte(s, 3), byte(s, 4), byte(s, 5), byte(s, 6),
byte(s, 7), byte(s, 8), byte(s, 9), byte(s, 10), byte(s, 11), byte(s, 12), byte(s, 13),
byte(s, 14), byte(s, 15), byte(s, 16), byte(s, 17), byte(s, 18), byte(s, 19)}}};
}
template <typename T, char... c>
constexpr T from_literal() noexcept
{
constexpr auto size = sizeof...(c);
constexpr char literal[] = {c...};
constexpr bool is_simple_zero = size == 1 && literal[0] == '0';
static_assert(is_simple_zero || (literal[0] == '0' && literal[1] == 'x'),
"literal must be in hexadecimal notation");
static_assert(is_simple_zero || size == 2 * sizeof(T) + 2,
"literal must match the result type size");
return is_simple_zero ? T{} : from_hex<T>(&literal[2]);
}
} // namespace internal
/// Literal for evmc::address.
template <char... c>
constexpr address operator""_address() noexcept
constexpr address operator""_address(const char* s) noexcept
{
return internal::from_literal<address, c...>();
return parse<address>(s);
}
/// Literal for evmc::bytes32.
template <char... c>
constexpr bytes32 operator""_bytes32() noexcept
constexpr bytes32 operator""_bytes32(const char* s) noexcept
{
return internal::from_literal<bytes32, c...>();
return parse<bytes32>(s);
}
} // namespace literals
using namespace literals;
/// @copydoc evmc_status_code_to_string
inline const char* to_string(evmc_status_code status_code) noexcept
{
return evmc_status_code_to_string(status_code);
}
/// @copydoc evmc_revision_to_string
inline const char* to_string(evmc_revision rev) noexcept
{
return evmc_revision_to_string(rev);
}
/// Alias for evmc_make_result().
constexpr auto make_result = evmc_make_result;
@ -343,11 +328,12 @@ constexpr auto make_result = evmc_make_result;
///
/// This is a RAII wrapper for evmc_result and objects of this type
/// automatically release attached resources.
class result : private evmc_result
class Result : private evmc_result
{
public:
using evmc_result::create_address;
using evmc_result::gas_left;
using evmc_result::gas_refund;
using evmc_result::output_data;
using evmc_result::output_size;
using evmc_result::status_code;
@ -359,40 +345,70 @@ public:
///
/// @param _status_code The status code.
/// @param _gas_left The amount of gas left.
/// @param _gas_refund The amount of refunded gas.
/// @param _output_data The pointer to the output.
/// @param _output_size The output size.
result(evmc_status_code _status_code,
int64_t _gas_left,
const uint8_t* _output_data,
size_t _output_size) noexcept
: evmc_result{make_result(_status_code, _gas_left, _output_data, _output_size)}
explicit Result(evmc_status_code _status_code,
int64_t _gas_left,
int64_t _gas_refund,
const uint8_t* _output_data,
size_t _output_size) noexcept
: evmc_result{make_result(_status_code, _gas_left, _gas_refund, _output_data, _output_size)}
{}
/// Creates the result without output.
///
/// @param _status_code The status code.
/// @param _gas_left The amount of gas left.
/// @param _gas_refund The amount of refunded gas.
explicit Result(evmc_status_code _status_code = EVMC_INTERNAL_ERROR,
int64_t _gas_left = 0,
int64_t _gas_refund = 0) noexcept
: evmc_result{make_result(_status_code, _gas_left, _gas_refund, nullptr, 0)}
{}
/// Creates the result of contract creation.
///
/// @param _status_code The status code.
/// @param _gas_left The amount of gas left.
/// @param _gas_refund The amount of refunded gas.
/// @param _create_address The address of the possibly created account.
explicit Result(evmc_status_code _status_code,
int64_t _gas_left,
int64_t _gas_refund,
const evmc_address& _create_address) noexcept
: evmc_result{make_result(_status_code, _gas_left, _gas_refund, nullptr, 0)}
{
create_address = _create_address;
}
/// Converting constructor from raw evmc_result.
explicit result(evmc_result const& res) noexcept : evmc_result{res} {}
///
/// This object takes ownership of the resources of @p res.
explicit Result(const evmc_result& res) noexcept : evmc_result{res} {}
/// Destructor responsible for automatically releasing attached resources.
~result() noexcept
~Result() noexcept
{
if (release != nullptr)
release(this);
}
/// Move constructor.
result(result&& other) noexcept : evmc_result{other}
Result(Result&& other) noexcept : evmc_result{other}
{
other.release = nullptr; // Disable releasing of the rvalue object.
}
/// Move assignment operator.
///
/// The self-assigment MUST never happen.
/// The self-assignment MUST never happen.
///
/// @param other The other result object.
/// @return The reference to the left-hand side object.
result& operator=(result&& other) noexcept
Result& operator=(Result&& other) noexcept
{
this->~result(); // Release this object.
this->~Result(); // Release this object.
static_cast<evmc_result&>(*this) = other; // Copy data.
other.release = nullptr; // Disable releasing of the rvalue object.
return *this;
@ -448,10 +464,10 @@ public:
size_t buffer_size) const noexcept = 0;
/// @copydoc evmc_host_interface::selfdestruct
virtual void selfdestruct(const address& addr, const address& beneficiary) noexcept = 0;
virtual bool selfdestruct(const address& addr, const address& beneficiary) noexcept = 0;
/// @copydoc evmc_host_interface::call
virtual result call(const evmc_message& msg) noexcept = 0;
virtual Result call(const evmc_message& msg) noexcept = 0;
/// @copydoc evmc_host_interface::get_tx_context
virtual evmc_tx_context get_tx_context() const noexcept = 0;
@ -481,7 +497,6 @@ class HostContext : public HostInterface
{
const evmc_host_interface* host = nullptr;
evmc_host_context* context = nullptr;
mutable evmc_tx_context tx_context = {};
public:
/// Default constructor for null Host context.
@ -534,28 +549,18 @@ public:
return host->copy_code(context, &address, code_offset, buffer_data, buffer_size);
}
void selfdestruct(const address& addr, const address& beneficiary) noexcept final
bool selfdestruct(const address& addr, const address& beneficiary) noexcept final
{
host->selfdestruct(context, &addr, &beneficiary);
return host->selfdestruct(context, &addr, &beneficiary);
}
result call(const evmc_message& message) noexcept final
Result call(const evmc_message& message) noexcept final
{
return result{host->call(context, &message)};
return Result{host->call(context, &message)};
}
/// @copydoc HostInterface::get_tx_context()
///
/// The implementation caches the received transaction context
/// by assuming that the block timestamp should never be zero.
///
/// @return The cached transaction context.
evmc_tx_context get_tx_context() const noexcept final
{
if (tx_context.block_timestamp == 0)
tx_context = host->get_tx_context(context);
return tx_context;
}
evmc_tx_context get_tx_context() const noexcept final { return host->get_tx_context(context); }
bytes32 get_block_hash(int64_t number) const noexcept final
{
@ -685,18 +690,18 @@ public:
}
/// @copydoc evmc_execute()
result execute(const evmc_host_interface& host,
Result execute(const evmc_host_interface& host,
evmc_host_context* ctx,
evmc_revision rev,
const evmc_message& msg,
const uint8_t* code,
size_t code_size) noexcept
{
return result{m_instance->execute(m_instance, &host, ctx, rev, &msg, code, code_size)};
return Result{m_instance->execute(m_instance, &host, ctx, rev, &msg, code, code_size)};
}
/// Convenient variant of the VM::execute() that takes reference to evmc::Host class.
result execute(Host& host,
Result execute(Host& host,
evmc_revision rev,
const evmc_message& msg,
const uint8_t* code,
@ -713,12 +718,12 @@ public:
/// but without providing the Host context and interface.
/// This method is for experimental precompiles support where execution is
/// guaranteed not to require any Host access.
result execute(evmc_revision rev,
Result execute(evmc_revision rev,
const evmc_message& msg,
const uint8_t* code,
size_t code_size) noexcept
{
return result{
return Result{
m_instance->execute(m_instance, nullptr, nullptr, rev, &msg, code, code_size)};
}
@ -789,11 +794,11 @@ inline size_t copy_code(evmc_host_context* h,
return Host::from_context(h)->copy_code(*addr, code_offset, buffer_data, buffer_size);
}
inline void selfdestruct(evmc_host_context* h,
inline bool selfdestruct(evmc_host_context* h,
const evmc_address* addr,
const evmc_address* beneficiary) noexcept
{
Host::from_context(h)->selfdestruct(*addr, *beneficiary);
return Host::from_context(h)->selfdestruct(*addr, *beneficiary);
}
inline evmc_result call(evmc_host_context* h, const evmc_message* msg) noexcept
@ -837,7 +842,7 @@ inline evmc_access_status access_storage(evmc_host_context* h,
inline const evmc_host_interface& Host::get_interface() noexcept
{
static constexpr evmc_host_interface interface{
static constexpr evmc_host_interface interface = {
::evmc::internal::account_exists, ::evmc::internal::get_storage,
::evmc::internal::set_storage, ::evmc::internal::get_balance,
::evmc::internal::get_code_size, ::evmc::internal::get_code_hash,
@ -851,6 +856,24 @@ inline const evmc_host_interface& Host::get_interface() noexcept
} // namespace evmc
/// "Stream out" operator implementation for ::evmc_status_code.
///
/// @note This is defined in global namespace to match ::evmc_status_code definition and allow
/// convenient operator overloading usage.
inline std::ostream& operator<<(std::ostream& os, evmc_status_code status_code)
{
return os << evmc::to_string(status_code);
}
/// "Stream out" operator implementation for ::evmc_revision.
///
/// @note This is defined in global namespace to match ::evmc_revision definition and allow
/// convenient operator overloading usage.
inline std::ostream& operator<<(std::ostream& os, evmc_revision rev)
{
return os << evmc::to_string(rev);
}
namespace std
{
/// Hash operator template specialization for evmc::address. Needed for unordered containers.

View File

@ -0,0 +1,105 @@
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2022 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
#pragma once
#include <iterator>
namespace evmc
{
/// The constexpr variant of std::isspace().
inline constexpr bool isspace(char ch) noexcept
{
// Implementation taken from LLVM's libc.
return ch == ' ' || (static_cast<unsigned>(ch) - '\t') < 5;
}
/// Checks if a character is not a white space.
inline constexpr bool is_not_space(char ch) noexcept
{
return !isspace(ch);
}
/// The filter iterator adaptor creates a view of an iterator range in which some elements of the
/// range are skipped. A predicate function controls which elements are skipped. When the predicate
/// is applied to an element, if it returns true then the element is retained and if it returns
/// false then the element is skipped over. When skipping over elements, it is necessary for the
/// filter adaptor to know when to stop so as to avoid going past the end of the underlying range.
/// A filter iterator is therefore constructed with pair of iterators indicating the range of
/// elements in the unfiltered sequence to be traversed.
///
/// Similar to boost::filter_iterator.
template <typename BaseIterator,
bool predicate(typename std::iterator_traits<BaseIterator>::value_type) noexcept>
struct filter_iterator
{
/// The iterator difference type.
using difference_type = typename std::iterator_traits<BaseIterator>::difference_type;
/// The iterator value type.
using value_type = typename std::iterator_traits<BaseIterator>::value_type;
/// The iterator pointer type.
using pointer = typename std::iterator_traits<BaseIterator>::pointer;
/// The iterator reference type.
using reference = typename std::iterator_traits<BaseIterator>::reference;
/// The iterator category.
using iterator_category = std::input_iterator_tag;
private:
BaseIterator base;
BaseIterator base_end;
value_type value;
constexpr void forward_to_next_value() noexcept
{
for (; base != base_end; ++base)
{
value = *base;
if (predicate(value))
break;
}
}
public:
/// The constructor of the base iterator pair.
constexpr filter_iterator(BaseIterator it, BaseIterator end) noexcept : base{it}, base_end{end}
{
forward_to_next_value();
}
/// The dereference operator.
constexpr auto operator*() noexcept
{
// We should not read from an input base iterator twice. So the only read is in
// forward_to_next_value() and here we return the cached value.
return value;
}
/// The increment operator.
constexpr void operator++() noexcept
{
++base;
forward_to_next_value();
}
/// The equality operator.
constexpr bool operator==(const filter_iterator& o) const noexcept { return base == o.base; }
/// The inequality operator.
constexpr bool operator!=(const filter_iterator& o) const noexcept { return base != o.base; }
};
/// The input filter iterator which skips whitespace characters from the base input iterator.
template <typename BaseIterator>
struct skip_space_iterator : filter_iterator<BaseIterator, is_not_space>
{
using filter_iterator<BaseIterator, is_not_space>::filter_iterator;
};
/// Class template argument deduction guide.
template <typename BaseIterator>
skip_space_iterator(BaseIterator, BaseIterator) -> skip_space_iterator<BaseIterator>;
} // namespace evmc

View File

@ -1,7 +1,6 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018-2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
/**
* EVMC Helpers
@ -10,8 +9,6 @@
* These are convenient for languages where invoking function pointers
* is "ugly" or impossible (such as Go).
*
* It also contains helpers (overloaded operators) for using EVMC types effectively in C++.
*
* @defgroup helpers EVMC Helpers
* @{
*/
@ -21,6 +18,12 @@
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
/**
* Returns true if the VM has a compatible ABI version.
*/
@ -116,10 +119,12 @@ static void evmc_free_result_memory(const struct evmc_result* result)
///
/// @param status_code The status code.
/// @param gas_left The amount of gas left.
/// @param gas_refund The amount of refunded gas.
/// @param output_data The pointer to the output.
/// @param output_size The output size.
static inline struct evmc_result evmc_make_result(enum evmc_status_code status_code,
int64_t gas_left,
int64_t gas_refund,
const uint8_t* output_data,
size_t output_size)
{
@ -144,6 +149,7 @@ static inline struct evmc_result evmc_make_result(enum evmc_status_code status_c
result.status_code = status_code;
result.gas_left = gas_left;
result.gas_refund = gas_refund;
return result;
}
@ -209,4 +215,95 @@ static inline const union evmc_result_optional_storage* evmc_get_const_optional_
/** @} */
/** Returns text representation of the ::evmc_status_code. */
static inline const char* evmc_status_code_to_string(enum evmc_status_code status_code)
{
switch (status_code)
{
case EVMC_SUCCESS:
return "success";
case EVMC_FAILURE:
return "failure";
case EVMC_REVERT:
return "revert";
case EVMC_OUT_OF_GAS:
return "out of gas";
case EVMC_INVALID_INSTRUCTION:
return "invalid instruction";
case EVMC_UNDEFINED_INSTRUCTION:
return "undefined instruction";
case EVMC_STACK_OVERFLOW:
return "stack overflow";
case EVMC_STACK_UNDERFLOW:
return "stack underflow";
case EVMC_BAD_JUMP_DESTINATION:
return "bad jump destination";
case EVMC_INVALID_MEMORY_ACCESS:
return "invalid memory access";
case EVMC_CALL_DEPTH_EXCEEDED:
return "call depth exceeded";
case EVMC_STATIC_MODE_VIOLATION:
return "static mode violation";
case EVMC_PRECOMPILE_FAILURE:
return "precompile failure";
case EVMC_CONTRACT_VALIDATION_FAILURE:
return "contract validation failure";
case EVMC_ARGUMENT_OUT_OF_RANGE:
return "argument out of range";
case EVMC_WASM_UNREACHABLE_INSTRUCTION:
return "wasm unreachable instruction";
case EVMC_WASM_TRAP:
return "wasm trap";
case EVMC_INSUFFICIENT_BALANCE:
return "insufficient balance";
case EVMC_INTERNAL_ERROR:
return "internal error";
case EVMC_REJECTED:
return "rejected";
case EVMC_OUT_OF_MEMORY:
return "out of memory";
}
return "<unknown>";
}
/** Returns the name of the ::evmc_revision. */
static inline const char* evmc_revision_to_string(enum evmc_revision rev)
{
switch (rev)
{
case EVMC_FRONTIER:
return "Frontier";
case EVMC_HOMESTEAD:
return "Homestead";
case EVMC_TANGERINE_WHISTLE:
return "Tangerine Whistle";
case EVMC_SPURIOUS_DRAGON:
return "Spurious Dragon";
case EVMC_BYZANTIUM:
return "Byzantium";
case EVMC_CONSTANTINOPLE:
return "Constantinople";
case EVMC_PETERSBURG:
return "Petersburg";
case EVMC_ISTANBUL:
return "Istanbul";
case EVMC_BERLIN:
return "Berlin";
case EVMC_LONDON:
return "London";
case EVMC_PARIS:
return "Paris";
case EVMC_SHANGHAI:
return "Shanghai";
case EVMC_CANCUN:
return "Cancun";
}
return "<unknown>";
}
/** @} */
#ifdef __cplusplus
#pragma GCC diagnostic pop
} // extern "C"
#endif

158
test/evmc/hex.hpp Normal file
View File

@ -0,0 +1,158 @@
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2021 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
#pragma once
#include <evmc/filter_iterator.hpp>
#include <cstdint>
#include <optional>
#include <string>
#include <string_view>
namespace evmc
{
/// String of uint8_t chars.
using bytes = std::basic_string<uint8_t>;
/// String view of uint8_t chars.
using bytes_view = std::basic_string_view<uint8_t>;
/// Encode a byte to a hex string.
inline std::string hex(uint8_t b) noexcept
{
static constexpr auto hex_digits = "0123456789abcdef";
return {hex_digits[b >> 4], hex_digits[b & 0xf]};
}
/// Encodes bytes as hex string.
inline std::string hex(bytes_view bs)
{
std::string str;
str.reserve(bs.size() * 2);
for (const auto b : bs)
str += hex(b);
return str;
}
namespace internal
{
/// Extracts the nibble value out of a hex digit.
/// Returns -1 in case of invalid hex digit.
inline constexpr int from_hex_digit(char h) noexcept
{
if (h >= '0' && h <= '9')
return h - '0';
else if (h >= 'a' && h <= 'f')
return h - 'a' + 10;
else if (h >= 'A' && h <= 'F')
return h - 'A' + 10;
else
return -1;
}
} // namespace internal
/// Decodes hex-encoded sequence of characters.
///
/// It is guaranteed that the output will not be longer than half of the input length.
///
/// @param begin The input begin iterator. It only must satisfy input iterator concept.
/// @param end The input end iterator. It only must satisfy input iterator concept.
/// @param out The output iterator. It must satisfy output iterator concept.
/// @return True if successful, false if input is invalid hex.
template <typename InputIt, typename OutputIt>
inline constexpr bool from_hex(InputIt begin, InputIt end, OutputIt out) noexcept
{
int hi_nibble = -1; // Init with invalid value, should never be used.
size_t i = 0;
for (auto it = begin; it != end; ++it, ++i)
{
const auto h = *it;
const int v = evmc::internal::from_hex_digit(h);
if (v < 0)
{
if (i == 1 && hi_nibble == 0 && h == 'x') // 0x prefix
continue;
return false;
}
if (i % 2 == 0)
hi_nibble = v << 4;
else
*out++ = static_cast<uint8_t>(hi_nibble | v);
}
return i % 2 == 0;
}
/// Validates hex encoded string.
///
/// @return True if the input is valid hex.
inline bool validate_hex(std::string_view hex) noexcept
{
struct noop_output_iterator
{
uint8_t sink = {};
uint8_t& operator*() noexcept { return sink; }
noop_output_iterator operator++(int) noexcept { return *this; } // NOLINT(cert-dcl21-cpp)
};
return from_hex(hex.begin(), hex.end(), noop_output_iterator{});
}
/// Decodes hex encoded string to bytes.
///
/// In case the input is invalid the returned value is std::nullopt.
/// This can happen if a non-hex digit or odd number of digits is encountered.
inline std::optional<bytes> from_hex(std::string_view hex)
{
bytes bs;
bs.reserve(hex.size() / 2);
if (!from_hex(hex.begin(), hex.end(), std::back_inserter(bs)))
return {};
return bs;
}
/// Decodes hex-encoded string into custom type T with .bytes array of uint8_t.
///
/// When the input is smaller than the result type, the result is padded with zeros on the left
/// (the result bytes of lowest indices are filled with zeros).
/// TODO: Support optional left alignment.
template <typename T>
constexpr std::optional<T> from_hex(std::string_view s) noexcept
{
// Omit the optional 0x prefix.
if (s.size() >= 2 && s[0] == '0' && s[1] == 'x')
s.remove_prefix(2);
T r{}; // The T must have .bytes array. This may be lifted if std::bit_cast is available.
constexpr auto num_out_bytes = std::size(r.bytes);
const auto num_in_bytes = s.length() / 2;
if (num_in_bytes > num_out_bytes)
return {};
if (!from_hex(s.begin(), s.end(), &r.bytes[num_out_bytes - num_in_bytes]))
return {};
return r;
}
/// Decodes hex encoded string to bytes. The whitespace in the input is ignored.
///
/// In case the input is invalid the returned value is std::nullopt.
/// This can happen if a non-hex digit or odd number of digits is encountered.
/// The whitespace (as defined by std::isspace) in the input is ignored.
template <typename InputIterator>
std::optional<bytes> from_spaced_hex(InputIterator begin, InputIterator end) noexcept
{
bytes bs;
if (!from_hex(skip_space_iterator{begin, end}, skip_space_iterator{end, end},
std::back_inserter(bs)))
return {};
return bs;
}
/// @copydoc from_spaced_hex
inline std::optional<bytes> from_spaced_hex(std::string_view hex) noexcept
{
return from_spaced_hex(hex.begin(), hex.end());
}
} // namespace evmc

View File

@ -1,7 +1,6 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018-2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
#include <evmc/loader.h>
@ -27,6 +26,7 @@
#define DLL_HANDLE void*
#define DLL_OPEN(filename) dlopen(filename, RTLD_LAZY)
#define DLL_CLOSE(handle) dlclose(handle)
// NOLINTNEXTLINE(performance-no-int-to-ptr)
#define DLL_GET_CREATE_FN(handle, name) (evmc_create_fn)(uintptr_t) dlsym(handle, name)
#define DLL_GET_ERROR_MSG() dlerror()
#endif
@ -60,17 +60,20 @@ static
dest[0] = 0;
return 1;
}
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
memcpy(dest, src, len);
dest[len] = 0;
return 0;
}
#define PATH_MAX_LENGTH 4096
enum
{
PATH_MAX_LENGTH = 4096,
LAST_ERROR_MSG_BUFFER_SIZE = 511
};
static const char* last_error_msg = NULL;
#define LAST_ERROR_MSG_BUFFER_SIZE 511
// Buffer for formatted error messages.
// It has one null byte extra to avoid buffer read overflow during concurrent access.
static char last_error_msg_buffer[LAST_ERROR_MSG_BUFFER_SIZE + 1];
@ -82,6 +85,7 @@ static enum evmc_loader_error_code set_error(enum evmc_loader_error_code error_c
{
va_list args;
va_start(args, format);
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
if (vsnprintf(last_error_msg_buffer, LAST_ERROR_MSG_BUFFER_SIZE, format, args) <
LAST_ERROR_MSG_BUFFER_SIZE)
last_error_msg = last_error_msg_buffer;
@ -181,7 +185,7 @@ exit:
return create_fn;
}
const char* evmc_last_error_msg()
const char* evmc_last_error_msg(void)
{
const char* m = last_error_msg;
last_error_msg = NULL;
@ -267,16 +271,15 @@ struct evmc_vm* evmc_load_and_configure(const char* config, enum evmc_loader_err
if (!vm)
return NULL;
if (vm->set_option == NULL && strlen(options) != 0)
{
ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s) does not support any options",
vm->name, path);
goto exit;
}
while (strlen(options) != 0)
{
if (vm->set_option == NULL)
{
ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s) does not support any options",
vm->name, path);
goto exit;
}
char* option = get_token(&options, ',');
// Slit option into name and value by taking the name token.

View File

@ -1,7 +1,6 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018-2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
/**
* EVMC Loader Library
@ -21,7 +20,10 @@ extern "C" {
/** The function pointer type for EVMC create functions. */
typedef struct evmc_vm* (*evmc_create_fn)(void);
/** Error codes for the EVMC loader. */
/// Error codes for the EVMC loader.
///
/// Objects of this type SHOULD be initialized with ::EVMC_LOADER_UNSPECIFIED_ERROR
/// before passing to the EVMC loader.
enum evmc_loader_error_code
{
/** The loader succeeded. */
@ -46,7 +48,11 @@ enum evmc_loader_error_code
EVMC_LOADER_INVALID_OPTION_NAME = 6,
/** The VM option value is invalid. */
EVMC_LOADER_INVALID_OPTION_VALUE = 7
EVMC_LOADER_INVALID_OPTION_VALUE = 7,
/// This error value will be never returned by the EVMC loader,
/// but can be used by users to init evmc_loader_error_code objects.
EVMC_LOADER_UNSPECIFIED_ERROR = -1
};
/**

View File

@ -5,6 +5,7 @@
#include <evmc/evmc.hpp>
#include <algorithm>
#include <cassert>
#include <string>
#include <map>
#include <unordered_map>
@ -16,7 +17,7 @@ namespace evmc
using bytes = std::basic_string<uint8_t>;
/// Extended value (with original value and access flag) for account storage.
struct storage_value
struct StorageValue
{
/// The current storage value.
bytes32 current;
@ -28,7 +29,20 @@ struct storage_value
evmc_access_status access_status = EVMC_ACCESS_COLD;
/// Default constructor.
storage_value() noexcept = default;
StorageValue() noexcept = default;
/// Constructor sets the current and original to the same value. Optional access status.
StorageValue(const bytes32& _value, // NOLINT(hicpp-explicit-conversions)
evmc_access_status _access_status = EVMC_ACCESS_COLD) noexcept
: current{_value}, original{_value}, access_status{_access_status}
{}
/// Constructor with original value and optional access status
StorageValue(const bytes32& _value,
const bytes32& _original,
evmc_access_status _access_status = EVMC_ACCESS_COLD) noexcept
: current{_value}, original{_original}, access_status{_access_status}
{}
};
/// Mocked account.
@ -47,7 +61,7 @@ struct MockedAccount
uint256be balance;
/// The account storage map.
std::map<bytes32, storage_value> storage;
std::map<bytes32, StorageValue> storage;
/// Helper method for setting balance by numeric type.
void set_balance(uint64_t x) noexcept
@ -81,22 +95,6 @@ public:
}
};
/// SELFDESTRUCT record.
struct selfdestruct_record
{
/// The address of the account which has self-destructed.
address selfdestructed;
/// The address of the beneficiary account.
address beneficiary;
/// Equal operator.
bool operator==(const selfdestruct_record& other) const noexcept
{
return selfdestructed == other.selfdestructed && beneficiary == other.beneficiary;
}
};
/// The set of all accounts in the Host, organized by their addresses.
std::unordered_map<address, MockedAccount> accounts;
@ -129,8 +127,9 @@ public:
/// The record of all LOGs passed to the emit_log() method.
std::vector<log_record> recorded_logs;
/// The record of all SELFDESTRUCTs from the selfdestruct() method.
std::vector<selfdestruct_record> recorded_selfdestructs;
/// The record of all SELFDESTRUCTs from the selfdestruct() method
/// as a map selfdestructed_address => [beneficiary1, beneficiary2, ...].
std::unordered_map<address, std::vector<address>> recorded_selfdestructs;
private:
/// The copy of call inputs for the recorded_calls record.
@ -176,32 +175,142 @@ public:
const bytes32& value) noexcept override
{
record_account_access(addr);
const auto it = accounts.find(addr);
if (it == accounts.end())
return EVMC_STORAGE_UNCHANGED;
auto& old = it->second.storage[key];
// Get the reference to the storage entry value.
// This will create the account in case it was not present.
// This is convenient for unit testing and standalone EVM execution to preserve the
// storage values after the execution terminates.
auto& s = accounts[addr].storage[key];
// Follow https://eips.ethereum.org/EIPS/eip-1283 specification.
// WARNING! This is not complete implementation as refund is not handled here.
// Follow the EIP-2200 specification as closely as possible.
// https://eips.ethereum.org/EIPS/eip-2200
// Warning: this is not the most efficient implementation. The storage status can be
// figured out by combining only 4 checks:
// - original != current (dirty)
// - original == value (restored)
// - current != 0
// - value != 0
const auto status = [&original = s.original, &current = s.current, &value]() {
// Clause 1 is irrelevant:
// 1. "If gasleft is less than or equal to gas stipend,
// fail the current call frame with out of gas exception"
if (old.current == value)
return EVMC_STORAGE_UNCHANGED;
evmc_storage_status status{};
if (old.original == old.current) // Storage slot not dirty
{
if (!old.current)
status = EVMC_STORAGE_ADDED;
else if (value)
status = EVMC_STORAGE_MODIFIED;
// 2. "If current value equals new value (this is a no-op)"
if (current == value)
{
// "SLOAD_GAS is deducted"
return EVMC_STORAGE_ASSIGNED;
}
// 3. "If current value does not equal new value"
else
status = EVMC_STORAGE_DELETED;
}
else
status = EVMC_STORAGE_MODIFIED_AGAIN;
{
// 3.1. "If original value equals current value
// (this storage slot has not been changed by the current execution context)"
if (original == current)
{
// 3.1.1 "If original value is 0"
if (is_zero(original))
{
// "SSTORE_SET_GAS is deducted"
return EVMC_STORAGE_ADDED;
}
// 3.1.2 "Otherwise"
else
{
// "SSTORE_RESET_GAS gas is deducted"
auto st = EVMC_STORAGE_MODIFIED;
old.current = value;
// "If new value is 0"
if (is_zero(value))
{
// "add SSTORE_CLEARS_SCHEDULE gas to refund counter"
st = EVMC_STORAGE_DELETED;
}
return st;
}
}
// 3.2. "If original value does not equal current value
// (this storage slot is dirty),
// SLOAD_GAS gas is deducted.
// Apply both of the following clauses."
else
{
// Because we need to apply "both following clauses"
// we first collect information which clause is triggered
// then assign status code to combination of these clauses.
enum
{
None = 0,
RemoveClearsSchedule = 1 << 0,
AddClearsSchedule = 1 << 1,
RestoredBySet = 1 << 2,
RestoredByReset = 1 << 3,
};
int triggered_clauses = None;
// 3.2.1. "If original value is not 0"
if (!is_zero(original))
{
// 3.2.1.1. "If current value is 0"
if (is_zero(current))
{
// "(also means that new value is not 0)"
assert(!is_zero(value));
// "remove SSTORE_CLEARS_SCHEDULE gas from refund counter"
triggered_clauses |= RemoveClearsSchedule;
}
// 3.2.1.2. "If new value is 0"
if (is_zero(value))
{
// "(also means that current value is not 0)"
assert(!is_zero(current));
// "add SSTORE_CLEARS_SCHEDULE gas to refund counter"
triggered_clauses |= AddClearsSchedule;
}
}
// 3.2.2. "If original value equals new value (this storage slot is reset)"
// Except: we use term 'storage slot restored'.
if (original == value)
{
// 3.2.2.1. "If original value is 0"
if (is_zero(original))
{
// "add SSTORE_SET_GAS - SLOAD_GAS to refund counter"
triggered_clauses |= RestoredBySet;
}
// 3.2.2.2. "Otherwise"
else
{
// "add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter"
triggered_clauses |= RestoredByReset;
}
}
switch (triggered_clauses)
{
case RemoveClearsSchedule:
return EVMC_STORAGE_DELETED_ADDED;
case AddClearsSchedule:
return EVMC_STORAGE_MODIFIED_DELETED;
case RemoveClearsSchedule | RestoredByReset:
return EVMC_STORAGE_DELETED_RESTORED;
case RestoredBySet:
return EVMC_STORAGE_ADDED_DELETED;
case RestoredByReset:
return EVMC_STORAGE_MODIFIED_RESTORED;
case None:
return EVMC_STORAGE_ASSIGNED;
default:
assert(false); // Other combinations are impossible.
return evmc_storage_status{};
}
}
}
}();
s.current = value; // Finally update the current storage value.
return status;
}
@ -260,16 +369,18 @@ public:
}
/// Selfdestruct the account (EVMC host method).
void selfdestruct(const address& addr, const address& beneficiary) noexcept override
bool selfdestruct(const address& addr, const address& beneficiary) noexcept override
{
record_account_access(addr);
recorded_selfdestructs.push_back({addr, beneficiary});
auto& beneficiaries = recorded_selfdestructs[addr];
beneficiaries.emplace_back(beneficiary);
return beneficiaries.size() == 1;
}
/// Call/create other contract (EVMC host method).
result call(const evmc_message& msg) noexcept override
Result call(const evmc_message& msg) noexcept override
{
record_account_access(msg.destination);
record_account_access(msg.recipient);
if (recorded_calls.empty())
{
@ -288,7 +399,7 @@ public:
call_msg.input_data = input_copy.data();
}
}
return result{call_result};
return Result{call_result};
}
/// Get transaction context (EVMC host method).
@ -316,11 +427,11 @@ public:
/// This method is required by EIP-2929 introduced in ::EVMC_BERLIN. It will record the account
/// access in MockedHost::recorded_account_accesses and return previous access status.
/// This methods returns ::EVMC_ACCESS_WARM for known addresses of precompiles.
/// The EIP-2929 specifies that evmc_message::sender and evmc_message::destination are always
/// The EIP-2929 specifies that evmc_message::sender and evmc_message::recipient are always
/// ::EVMC_ACCESS_WARM. Therefore, you should init the MockedHost with:
///
/// mocked_host.access_account(msg.sender);
/// mocked_host.access_account(msg.destination);
/// mocked_host.access_account(msg.recipient);
///
/// The same way you can mock transaction access list (EIP-2930) for account addresses.
///
@ -346,16 +457,18 @@ public:
/// Access the account's storage value at the given key.
///
/// This method is required by EIP-2929 introduced in ::EVMC_BERLIN. In records that the given
/// account's storage key has been access and returns the previous access status.
/// To mock storage access list (EIP-2930), you can pre-init account's storage values with
/// the ::EVMC_ACCESS_WARM flag:
/// This method is required by EIP-2929 introduced in ::EVMC_BERLIN. In records
/// that the given account's storage key has been access and returns the
/// previous access status. To mock storage access list (EIP-2930), you can
/// pre-init account's storage values with the ::EVMC_ACCESS_WARM flag:
///
/// mocked_host.accounts[msg.destination].storage[key] = {value, EVMC_ACCESS_WARM};
/// mocked_host.accounts[msg.recipient].storage[key] = {value,
/// EVMC_ACCESS_WARM};
///
/// @param addr The account address.
/// @param key The account's storage key.
/// @return The ::EVMC_ACCESS_WARM if the storage key has been accessed before,
/// @return The ::EVMC_ACCESS_WARM if the storage key has been accessed
/// before,
/// the ::EVMC_ACCESS_COLD otherwise.
evmc_access_status access_storage(const address& addr, const bytes32& key) noexcept override
{

View File

@ -1,7 +1,6 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018-2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2018 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
#pragma once

View File

@ -83,31 +83,32 @@ evmc_message EvmoneUtility::initializeMessage(bytes const& _input)
return msg;
}
evmc::result EvmoneUtility::executeContract(
evmc::Result EvmoneUtility::executeContract(
bytes const& _functionHash,
evmc_address _deployedAddress
)
{
evmc_message message = initializeMessage(_functionHash);
message.destination = _deployedAddress;
message.recipient = _deployedAddress;
message.code_address = _deployedAddress;
message.kind = EVMC_CALL;
return m_evmHost.call(message);
}
evmc::result EvmoneUtility::deployContract(bytes const& _code)
evmc::Result EvmoneUtility::deployContract(bytes const& _code)
{
evmc_message message = initializeMessage(_code);
message.kind = EVMC_CREATE;
return m_evmHost.call(message);
}
evmc::result EvmoneUtility::deployAndExecute(
evmc::Result EvmoneUtility::deployAndExecute(
bytes const& _byteCode,
string const& _hexEncodedInput
)
{
// Deploy contract and signal failure if deploy failed
evmc::result createResult = deployContract(_byteCode);
evmc::Result createResult = deployContract(_byteCode);
solAssert(
createResult.status_code == EVMC_SUCCESS,
"SolidityEvmoneInterface: Contract creation failed"
@ -115,7 +116,7 @@ evmc::result EvmoneUtility::deployAndExecute(
// Execute test function and signal failure if EVM reverted or
// did not return expected output on successful execution.
evmc::result callResult = executeContract(
evmc::Result callResult = executeContract(
util::fromHex(_hexEncodedInput),
createResult.create_address
);
@ -128,7 +129,7 @@ evmc::result EvmoneUtility::deployAndExecute(
return callResult;
}
evmc::result EvmoneUtility::compileDeployAndExecute(string _fuzzIsabelle)
evmc::Result EvmoneUtility::compileDeployAndExecute(string _fuzzIsabelle)
{
map<string, h160> libraryAddressMap;
// Stage 1: Compile and deploy library if present.
@ -139,7 +140,7 @@ evmc::result EvmoneUtility::compileDeployAndExecute(string _fuzzIsabelle)
solAssert(compilationOutput.has_value(), "Compiling library failed");
CompilerOutput cOutput = compilationOutput.value();
// Deploy contract and signal failure if deploy failed
evmc::result createResult = deployContract(cOutput.byteCode);
evmc::Result createResult = deployContract(cOutput.byteCode);
solAssert(
createResult.status_code == EVMC_SUCCESS,
"SolidityEvmoneInterface: Library deployment failed"

View File

@ -122,7 +122,7 @@ public:
/// and executing test configuration.
/// @param _isabelleData contains encoding data to be passed to the
/// isabelle test entry point.
evmc::result compileDeployAndExecute(std::string _isabelleData = {});
evmc::Result compileDeployAndExecute(std::string _isabelleData = {});
/// Compares the contents of the memory address pointed to
/// by `_result` of `_length` bytes to u256 zero.
/// @returns true if `_result` is zero, false
@ -138,17 +138,17 @@ private:
/// @returns the result of the execution of the function whose
/// keccak256 hash is @param _functionHash that is deployed at
/// @param _deployedAddress in @param _hostContext.
evmc::result executeContract(
evmc::Result executeContract(
bytes const& _functionHash,
evmc_address _deployedAddress
);
/// @returns the result of deployment of @param _code on @param _hostContext.
evmc::result deployContract(bytes const& _code);
evmc::Result deployContract(bytes const& _code);
/// Deploys and executes EVM byte code in @param _byteCode on
/// EVM Host referenced by @param _hostContext. Input passed
/// to execution context is @param _hexEncodedInput.
/// @returns result returning by @param _hostContext.
evmc::result deployAndExecute(
evmc::Result deployAndExecute(
bytes const& _byteCode,
std::string const& _hexEncodedInput
);

View File

@ -87,11 +87,11 @@ DEFINE_PROTO_FUZZER(Program const& _input)
return;
}
evmc::result deployResult = YulEvmoneUtility{}.deployCode(unoptimisedByteCode, hostContext);
evmc::Result deployResult = YulEvmoneUtility{}.deployCode(unoptimisedByteCode, hostContext);
if (deployResult.status_code != EVMC_SUCCESS)
return;
auto callMessage = YulEvmoneUtility{}.callMessage(deployResult.create_address);
evmc::result callResult = hostContext.call(callMessage);
evmc::Result callResult = hostContext.call(callMessage);
// If the fuzzer synthesized input does not contain the revert opcode which
// we lazily check by string find, the EVM call should not revert.
bool noRevertInSource = yul_source.find("revert") == string::npos;
@ -132,13 +132,13 @@ DEFINE_PROTO_FUZZER(Program const& _input)
// Reset host before running optimised code.
hostContext.reset();
evmc::result deployResultOpt = YulEvmoneUtility{}.deployCode(optimisedByteCode, hostContext);
evmc::Result deployResultOpt = YulEvmoneUtility{}.deployCode(optimisedByteCode, hostContext);
solAssert(
deployResultOpt.status_code == EVMC_SUCCESS,
"Evmone: Optimized contract creation failed"
);
auto callMessageOpt = YulEvmoneUtility{}.callMessage(deployResultOpt.create_address);
evmc::result callResultOpt = hostContext.call(callMessageOpt);
evmc::Result callResultOpt = hostContext.call(callMessageOpt);
if (noRevertInSource)
solAssert(
callResultOpt.status_code != EVMC_REVERT,

View File

@ -38,7 +38,7 @@ bytes YulAssembler::assemble()
return m_stack.assemble(YulStack::Machine::EVM).bytecode->bytecode;
}
evmc::result YulEvmoneUtility::deployCode(bytes const& _input, EVMHost& _host)
evmc::Result YulEvmoneUtility::deployCode(bytes const& _input, EVMHost& _host)
{
// Zero initialize all message fields
evmc_message msg = {};
@ -74,7 +74,8 @@ evmc_message YulEvmoneUtility::callMessage(evmc_address _address)
{
evmc_message call = {};
call.gas = std::numeric_limits<int64_t>::max();
call.destination = _address;
call.recipient = _address;
call.code_address = _address;
call.kind = EVMC_CALL;
return call;
}

View File

@ -54,7 +54,7 @@ private:
struct YulEvmoneUtility
{
/// @returns the result of deploying bytecode @param _input on @param _host.
static evmc::result deployCode(solidity::bytes const& _input, EVMHost& _host);
static evmc::Result deployCode(solidity::bytes const& _input, EVMHost& _host);
/// @returns call message to be sent to @param _address.
static evmc_message callMessage(evmc_address _address);
/// @returns true if call result indicates a serious error, false otherwise.