mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #13532 from ethereum/evmc-update
Upgrade to evmc10 + evmone9
This commit is contained in:
commit
2fb7205dd3
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {};
|
||||
|
107
test/EVMHost.cpp
107
test/EVMHost.cpp
@ -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,9 +1199,10 @@ void EVMHostPrinter::balance()
|
||||
void EVMHostPrinter::selfdestructRecords()
|
||||
{
|
||||
for (auto const& record: m_host.recorded_selfdestructs)
|
||||
for (auto const& beneficiary: record.second)
|
||||
m_stateStream << "SELFDESTRUCT"
|
||||
<< " BENEFICIARY "
|
||||
<< m_host.convertFromEVMC(record.beneficiary)
|
||||
<< m_host.convertFromEVMC(beneficiary)
|
||||
<< endl;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
201
test/evmc/evmc.h
201
test/evmc/evmc.h
@ -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,15 +159,34 @@ 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;
|
||||
};
|
||||
|
||||
|
||||
@ -152,7 +199,7 @@ struct evmc_tx_context
|
||||
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 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
|
||||
|
@ -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,
|
||||
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, _output_data, _output_size)}
|
||||
: 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.
|
||||
|
105
test/evmc/filter_iterator.hpp
Normal file
105
test/evmc/filter_iterator.hpp
Normal 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
|
@ -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
158
test/evmc/hex.hpp
Normal 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
|
@ -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)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
while (strlen(options) != 0)
|
||||
{
|
||||
char* option = get_token(&options, ',');
|
||||
|
||||
// Slit option into name and value by taking the name token.
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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, ¤t = 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
|
||||
// 2. "If current value equals new value (this is a no-op)"
|
||||
if (current == value)
|
||||
{
|
||||
if (!old.current)
|
||||
status = EVMC_STORAGE_ADDED;
|
||||
else if (value)
|
||||
status = EVMC_STORAGE_MODIFIED;
|
||||
else
|
||||
status = EVMC_STORAGE_DELETED;
|
||||
// "SLOAD_GAS is deducted"
|
||||
return EVMC_STORAGE_ASSIGNED;
|
||||
}
|
||||
// 3. "If current value does not equal new value"
|
||||
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
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user