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:
|
parameters:
|
||||||
ubuntu-2004-docker-image:
|
ubuntu-2004-docker-image:
|
||||||
type: string
|
type: string
|
||||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-14
|
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-15
|
||||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d1ef23849db4c5462b248d89c111da4009b153cbd5002cb8755b0580312be581"
|
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:e4f83457bf1d6475c3189e9013da77289793a5ecd6a0e15dbec9411880b11e22"
|
||||||
ubuntu-2004-clang-docker-image:
|
ubuntu-2004-clang-docker-image:
|
||||||
type: string
|
type: string
|
||||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-14
|
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-15
|
||||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:beb8c91998ec0df99a488900b3723a06f1122f0954fc73786b6c53fd73a6408d"
|
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:8dda4fdae312f840fbb4e25b9ef01ad3209e9014e49e4564ab0f0d2510225131"
|
||||||
ubuntu-1604-clang-ossfuzz-docker-image:
|
ubuntu-1604-clang-ossfuzz-docker-image:
|
||||||
type: string
|
type: string
|
||||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-19
|
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-20
|
||||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:8c9bf1813c261d781f4c65fceed2dfb3ecf5be9ecf49bddbd250b570a7f3baea"
|
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:7b3ccaed3b5d37dc2cc4bbe1a1e40949266292dcdbc3ad015271ab4e5e6f5038"
|
||||||
emscripten-docker-image:
|
emscripten-docker-image:
|
||||||
type: string
|
type: string
|
||||||
# solbuildpackpusher/solidity-buildpack-deps:emscripten-13
|
# solbuildpackpusher/solidity-buildpack-deps:emscripten-13
|
||||||
|
@ -74,18 +74,18 @@ then
|
|||||||
rm -r "$z3_dir"
|
rm -r "$z3_dir"
|
||||||
|
|
||||||
# evmone
|
# evmone
|
||||||
evmone_version="0.8.0"
|
evmone_version="0.9.1"
|
||||||
evmone_package="evmone-${evmone_version}-darwin-x86_64.tar.gz"
|
evmone_package="evmone-${evmone_version}-darwin-x86_64.tar.gz"
|
||||||
wget "https://github.com/ethereum/evmone/releases/download/v${evmone_version}/${evmone_package}"
|
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
|
tar xzpf "$evmone_package" -C /usr/local
|
||||||
rm "$evmone_package"
|
rm "$evmone_package"
|
||||||
|
|
||||||
# hera
|
# hera
|
||||||
hera_version="0.5.0"
|
hera_version="0.6.0"
|
||||||
hera_package="hera-${hera_version}-darwin-x86_64.tar.gz"
|
hera_package="hera-${hera_version}-darwin-x86_64.tar.gz"
|
||||||
wget "https://github.com/ewasm/hera/releases/download/v${hera_version}/${hera_package}"
|
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
|
tar xzpf "$hera_package" -C /usr/local
|
||||||
rm "$hera_package"
|
rm "$hera_package"
|
||||||
fi
|
fi
|
||||||
|
@ -3,7 +3,7 @@ $ErrorActionPreference = "Stop"
|
|||||||
# Needed for Invoke-WebRequest to work via CI.
|
# Needed for Invoke-WebRequest to work via CI.
|
||||||
$progressPreference = "silentlyContinue"
|
$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"
|
tar -xf evmone.zip "bin/evmone.dll"
|
||||||
mkdir deps
|
mkdir deps
|
||||||
mv bin/evmone.dll deps
|
mv bin/evmone.dll deps
|
||||||
|
@ -33,19 +33,19 @@ namespace solidity::test
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static constexpr auto evmoneFilename = "evmone.dll";
|
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 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__)
|
#elif defined(__APPLE__)
|
||||||
static constexpr auto evmoneFilename = "libevmone.dylib";
|
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 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
|
#else
|
||||||
static constexpr auto evmoneFilename = "libevmone.so";
|
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 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
|
#endif
|
||||||
|
|
||||||
struct ConfigException: public util::Exception {};
|
struct ConfigException: public util::Exception {};
|
||||||
|
113
test/EVMHost.cpp
113
test/EVMHost.cpp
@ -122,10 +122,15 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm):
|
|||||||
m_evmRevision = EVMC_BERLIN;
|
m_evmRevision = EVMC_BERLIN;
|
||||||
else if (_evmVersion == langutil::EVMVersion::london())
|
else if (_evmVersion == langutil::EVMVersion::london())
|
||||||
m_evmRevision = EVMC_LONDON;
|
m_evmRevision = EVMC_LONDON;
|
||||||
|
// TODO: support EVMVersion::paris()
|
||||||
else
|
else
|
||||||
assertThrow(false, Exception, "Unsupported EVM version");
|
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_gas_limit = 20000000;
|
||||||
tx_context.block_coinbase = 0x7878787878787878787878787878787878787878_address;
|
tx_context.block_coinbase = 0x7878787878787878787878787878787878787878_address;
|
||||||
tx_context.tx_gas_price = evmc::uint256be{3000000000};
|
tx_context.tx_gas_price = evmc::uint256be{3000000000};
|
||||||
@ -145,7 +150,6 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm):
|
|||||||
void EVMHost::reset()
|
void EVMHost::reset()
|
||||||
{
|
{
|
||||||
accounts.clear();
|
accounts.clear();
|
||||||
m_currentAddress = {};
|
|
||||||
// Clear self destruct records
|
// Clear self destruct records
|
||||||
recorded_selfdestructs.clear();
|
recorded_selfdestructs.clear();
|
||||||
// Clear call records
|
// Clear call records
|
||||||
@ -194,14 +198,14 @@ void EVMHost::transfer(evmc::MockedAccount& _sender, evmc::MockedAccount& _recip
|
|||||||
_recipient.balance = convertToEVMC(u256(convertFromEVMC(_recipient.balance)) + _value);
|
_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.
|
// TODO actual selfdestruct is even more complicated.
|
||||||
|
|
||||||
transfer(accounts[_addr], accounts[_beneficiary], convertFromEVMC(accounts[_addr].balance));
|
transfer(accounts[_addr], accounts[_beneficiary], convertFromEVMC(accounts[_addr].balance));
|
||||||
|
|
||||||
// Record self destructs. Clearing will be done in newTransactionFrame().
|
// 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
|
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.
|
// NOTE: this is used for both internal and external calls.
|
||||||
// External calls are triggered from ExecutionFramework and contain only EVMC_CREATE or EVMC_CALL.
|
// 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);
|
recordCalls(_message);
|
||||||
if (_message.destination == 0x0000000000000000000000000000000000000001_address)
|
if (_message.recipient == 0x0000000000000000000000000000000000000001_address)
|
||||||
return precompileECRecover(_message);
|
return precompileECRecover(_message);
|
||||||
else if (_message.destination == 0x0000000000000000000000000000000000000002_address)
|
else if (_message.recipient == 0x0000000000000000000000000000000000000002_address)
|
||||||
return precompileSha256(_message);
|
return precompileSha256(_message);
|
||||||
else if (_message.destination == 0x0000000000000000000000000000000000000003_address)
|
else if (_message.recipient == 0x0000000000000000000000000000000000000003_address)
|
||||||
return precompileRipeMD160(_message);
|
return precompileRipeMD160(_message);
|
||||||
else if (_message.destination == 0x0000000000000000000000000000000000000004_address)
|
else if (_message.recipient == 0x0000000000000000000000000000000000000004_address)
|
||||||
return precompileIdentity(_message);
|
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);
|
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())
|
if (m_evmVersion <= langutil::EVMVersion::istanbul())
|
||||||
return precompileALTBN128G1Add<EVMC_ISTANBUL>(_message);
|
return precompileALTBN128G1Add<EVMC_ISTANBUL>(_message);
|
||||||
else
|
else
|
||||||
return precompileALTBN128G1Add<EVMC_LONDON>(_message);
|
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())
|
if (m_evmVersion <= langutil::EVMVersion::istanbul())
|
||||||
return precompileALTBN128G1Mul<EVMC_ISTANBUL>(_message);
|
return precompileALTBN128G1Mul<EVMC_ISTANBUL>(_message);
|
||||||
else
|
else
|
||||||
return precompileALTBN128G1Mul<EVMC_LONDON>(_message);
|
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())
|
if (m_evmVersion <= langutil::EVMVersion::istanbul())
|
||||||
return precompileALTBN128PairingProduct<EVMC_ISTANBUL>(_message);
|
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);
|
message.gas -= message.input_data[i] == 0 ? evmasm::GasCosts::txDataZeroGas : evmasm::GasCosts::txDataNonZeroGas(m_evmVersion);
|
||||||
if (message.gas < 0)
|
if (message.gas < 0)
|
||||||
{
|
{
|
||||||
evmc::result result({});
|
evmc::Result result;
|
||||||
result.status_code = EVMC_OUT_OF_GAS;
|
result.status_code = EVMC_OUT_OF_GAS;
|
||||||
accounts = stateBackup;
|
accounts = stateBackup;
|
||||||
return result;
|
return result;
|
||||||
@ -299,8 +303,8 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept
|
|||||||
encodedNonce
|
encodedNonce
|
||||||
), h160::AlignRight);
|
), h160::AlignRight);
|
||||||
|
|
||||||
message.destination = convertToEVMC(createAddress);
|
message.recipient = convertToEVMC(createAddress);
|
||||||
assertThrow(accounts.count(message.destination) == 0, Exception, "Account cannot exist");
|
assertThrow(accounts.count(message.recipient) == 0, Exception, "Account cannot exist");
|
||||||
|
|
||||||
code = evmc::bytes(message.input_data, message.input_data + message.input_size);
|
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()
|
keccak256(bytes(message.input_data, message.input_data + message.input_size)).asBytes()
|
||||||
), h160::AlignRight);
|
), h160::AlignRight);
|
||||||
|
|
||||||
message.destination = convertToEVMC(createAddress);
|
message.recipient = convertToEVMC(createAddress);
|
||||||
if (accounts.count(message.destination) && (
|
if (accounts.count(message.recipient) && (
|
||||||
accounts[message.destination].nonce > 0 ||
|
accounts[message.recipient].nonce > 0 ||
|
||||||
!accounts[message.destination].code.empty()
|
!accounts[message.recipient].code.empty()
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
evmc::result result({});
|
evmc::Result result;
|
||||||
result.status_code = EVMC_OUT_OF_GAS;
|
result.status_code = EVMC_OUT_OF_GAS;
|
||||||
accounts = stateBackup;
|
accounts = stateBackup;
|
||||||
return result;
|
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);
|
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
|
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 != 0 && message.kind != EVMC_DELEGATECALL && message.kind != EVMC_CALLCODE)
|
||||||
{
|
{
|
||||||
if (value > convertFromEVMC(sender.balance))
|
if (value > convertFromEVMC(sender.balance))
|
||||||
{
|
{
|
||||||
evmc::result result({});
|
evmc::Result result;
|
||||||
result.status_code = EVMC_INSUFFICIENT_BALANCE;
|
result.status_code = EVMC_INSUFFICIENT_BALANCE;
|
||||||
accounts = stateBackup;
|
accounts = stateBackup;
|
||||||
return result;
|
return result;
|
||||||
@ -355,12 +354,9 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept
|
|||||||
if (m_evmRevision >= EVMC_BERLIN)
|
if (m_evmRevision >= EVMC_BERLIN)
|
||||||
{
|
{
|
||||||
access_account(message.sender);
|
access_account(message.sender);
|
||||||
access_account(message.destination);
|
access_account(message.recipient);
|
||||||
}
|
}
|
||||||
evmc::address currentAddress = m_currentAddress;
|
evmc::Result result = m_vm.execute(*this, m_evmRevision, message, code.data(), code.size());
|
||||||
m_currentAddress = message.destination;
|
|
||||||
evmc::result result = m_vm.execute(*this, m_evmRevision, message, code.data(), code.size());
|
|
||||||
m_currentAddress = currentAddress;
|
|
||||||
|
|
||||||
if (message.kind == EVMC_CREATE || message.kind == EVMC_CREATE2)
|
if (message.kind == EVMC_CREATE || message.kind == EVMC_CREATE2)
|
||||||
{
|
{
|
||||||
@ -373,7 +369,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept
|
|||||||
}
|
}
|
||||||
else
|
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.code = evmc::bytes(result.output_data, result.output_data + result.output_size);
|
||||||
destination.codehash = convertToEVMC(keccak256({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;
|
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.
|
// 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
|
// ECRecover will return success with empty response in case of failure
|
||||||
if (result.status_code != EVMC_SUCCESS && result.status_code != EVMC_OUT_OF_GAS)
|
if (result.status_code != EVMC_SUCCESS && result.status_code != EVMC_OUT_OF_GAS)
|
||||||
// return resultWithGas(_message.gas, gas_cost, {});
|
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 result;
|
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...
|
// static data so that we do not need a release routine...
|
||||||
bytes static hash;
|
bytes static hash;
|
||||||
@ -477,7 +467,7 @@ evmc::result EVMHost::precompileSha256(evmc_message const& _message) noexcept
|
|||||||
return resultWithGas(_message.gas, gas_cost, hash);
|
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.
|
// 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);
|
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...
|
// static data so that we do not need a release routine...
|
||||||
bytes static data;
|
bytes static data;
|
||||||
@ -591,14 +581,14 @@ evmc::result EVMHost::precompileIdentity(evmc_message const& _message) noexcept
|
|||||||
return resultWithGas(_message.gas, gas_cost, data);
|
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
|
// TODO implement
|
||||||
return resultWithFailure();
|
return resultWithFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <evmc_revision Revision>
|
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.
|
// 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>
|
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.
|
// 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>
|
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.
|
// Base + per pairing gas.
|
||||||
constexpr auto calc_cost = [](unsigned points) -> int64_t {
|
constexpr auto calc_cost = [](unsigned points) -> int64_t {
|
||||||
@ -1124,7 +1114,7 @@ evmc::result EVMHost::precompileALTBN128PairingProduct(evmc_message const& _mess
|
|||||||
return precompileGeneric(_message, inputOutput);
|
return precompileGeneric(_message, inputOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
evmc::result EVMHost::precompileGeneric(
|
evmc::Result EVMHost::precompileGeneric(
|
||||||
evmc_message const& _message,
|
evmc_message const& _message,
|
||||||
map<bytes, EVMPrecompileOutput> const& _inOut) noexcept
|
map<bytes, EVMPrecompileOutput> const& _inOut) noexcept
|
||||||
{
|
{
|
||||||
@ -1138,20 +1128,20 @@ evmc::result EVMHost::precompileGeneric(
|
|||||||
return resultWithFailure();
|
return resultWithFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
evmc::result EVMHost::resultWithFailure() noexcept
|
evmc::Result EVMHost::resultWithFailure() noexcept
|
||||||
{
|
{
|
||||||
evmc::result result({});
|
evmc::Result result;
|
||||||
result.status_code = EVMC_FAILURE;
|
result.status_code = EVMC_FAILURE;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
evmc::result EVMHost::resultWithGas(
|
evmc::Result EVMHost::resultWithGas(
|
||||||
int64_t gas_limit,
|
int64_t gas_limit,
|
||||||
int64_t gas_required,
|
int64_t gas_required,
|
||||||
bytes const& _data
|
bytes const& _data
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
evmc::result result({});
|
evmc::Result result;
|
||||||
if (gas_limit < gas_required)
|
if (gas_limit < gas_required)
|
||||||
{
|
{
|
||||||
result.status_code = EVMC_OUT_OF_GAS;
|
result.status_code = EVMC_OUT_OF_GAS;
|
||||||
@ -1162,7 +1152,7 @@ evmc::result EVMHost::resultWithGas(
|
|||||||
result.status_code = EVMC_SUCCESS;
|
result.status_code = EVMC_SUCCESS;
|
||||||
result.gas_left = gas_limit - gas_required;
|
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();
|
result.output_size = _data.size();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -1209,10 +1199,11 @@ void EVMHostPrinter::balance()
|
|||||||
void EVMHostPrinter::selfdestructRecords()
|
void EVMHostPrinter::selfdestructRecords()
|
||||||
{
|
{
|
||||||
for (auto const& record: m_host.recorded_selfdestructs)
|
for (auto const& record: m_host.recorded_selfdestructs)
|
||||||
m_stateStream << "SELFDESTRUCT"
|
for (auto const& beneficiary: record.second)
|
||||||
<< " BENEFICIARY "
|
m_stateStream << "SELFDESTRUCT"
|
||||||
<< m_host.convertFromEVMC(record.beneficiary)
|
<< " BENEFICIARY "
|
||||||
<< endl;
|
<< m_host.convertFromEVMC(beneficiary)
|
||||||
|
<< endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EVMHostPrinter::callRecords()
|
void EVMHostPrinter::callRecords()
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
namespace solidity::test
|
namespace solidity::test
|
||||||
{
|
{
|
||||||
using Address = util::h160;
|
using Address = util::h160;
|
||||||
using StorageMap = std::map<evmc::bytes32, evmc::storage_value>;
|
using StorageMap = std::map<evmc::bytes32, evmc::StorageValue>;
|
||||||
|
|
||||||
struct EVMPrecompileOutput {
|
struct EVMPrecompileOutput {
|
||||||
bytes const output;
|
bytes const output;
|
||||||
@ -59,8 +59,8 @@ public:
|
|||||||
using MockedHost::access_storage;
|
using MockedHost::access_storage;
|
||||||
|
|
||||||
// Modified features of MockedHost.
|
// Modified features of MockedHost.
|
||||||
void selfdestruct(evmc::address const& _addr, evmc::address const& _beneficiary) noexcept final;
|
bool selfdestruct(evmc::address const& _addr, evmc::address const& _beneficiary) noexcept final;
|
||||||
evmc::result call(evmc_message const& _message) noexcept final;
|
evmc::Result call(evmc_message const& _message) noexcept final;
|
||||||
evmc::bytes32 get_block_hash(int64_t number) const noexcept final;
|
evmc::bytes32 get_block_hash(int64_t number) const noexcept final;
|
||||||
|
|
||||||
// Solidity testing specific features.
|
// Solidity testing specific features.
|
||||||
@ -98,35 +98,34 @@ public:
|
|||||||
static util::h256 convertFromEVMC(evmc::bytes32 const& _data);
|
static util::h256 convertFromEVMC(evmc::bytes32 const& _data);
|
||||||
static evmc::bytes32 convertToEVMC(util::h256 const& _data);
|
static evmc::bytes32 convertToEVMC(util::h256 const& _data);
|
||||||
private:
|
private:
|
||||||
evmc::address m_currentAddress = {};
|
|
||||||
|
|
||||||
/// Transfer value between accounts. Checks for sufficient balance.
|
/// Transfer value between accounts. Checks for sufficient balance.
|
||||||
void transfer(evmc::MockedAccount& _sender, evmc::MockedAccount& _recipient, u256 const& _value) noexcept;
|
void transfer(evmc::MockedAccount& _sender, evmc::MockedAccount& _recipient, u256 const& _value) noexcept;
|
||||||
|
|
||||||
/// Start a new transaction frame.
|
/// 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();
|
void newTransactionFrame();
|
||||||
|
|
||||||
/// Records calls made via @param _message.
|
/// Records calls made via @param _message.
|
||||||
void recordCalls(evmc_message const& _message) noexcept;
|
void recordCalls(evmc_message const& _message) noexcept;
|
||||||
|
|
||||||
static evmc::result precompileECRecover(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 precompileSha256(evmc_message const& _message) noexcept;
|
||||||
static evmc::result precompileRipeMD160(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 precompileIdentity(evmc_message const& _message) noexcept;
|
||||||
static evmc::result precompileModExp(evmc_message const& _message) noexcept;
|
static evmc::Result precompileModExp(evmc_message const& _message) noexcept;
|
||||||
template <evmc_revision Revision>
|
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>
|
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>
|
template <evmc_revision Revision>
|
||||||
static evmc::result precompileALTBN128PairingProduct(evmc_message const& _message) 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;
|
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.
|
/// @returns a result object with gas usage and result data taken from @a _data.
|
||||||
/// The outcome will be a failure if the limit < required.
|
/// The outcome will be a failure if the limit < required.
|
||||||
/// @note The return value is only valid as long as @a _data is alive!
|
/// @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 resultWithGas(int64_t gas_limit, int64_t gas_required, bytes const& _data) noexcept;
|
||||||
static evmc::result resultWithFailure() noexcept;
|
static evmc::Result resultWithFailure() noexcept;
|
||||||
|
|
||||||
evmc::VM& m_vm;
|
evmc::VM& m_vm;
|
||||||
/// EVM version requested by the testing tool
|
/// 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 << " value: " << _value << endl;
|
||||||
cout << " in: " << util::toHex(_data) << endl;
|
cout << " in: " << util::toHex(_data) << endl;
|
||||||
}
|
}
|
||||||
evmc_message message = {};
|
evmc_message message{};
|
||||||
message.input_data = _data.data();
|
message.input_data = _data.data();
|
||||||
message.input_size = _data.size();
|
message.input_size = _data.size();
|
||||||
message.sender = EVMHost::convertToEVMC(m_sender);
|
message.sender = EVMHost::convertToEVMC(m_sender);
|
||||||
@ -174,16 +174,19 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256
|
|||||||
if (_isCreation)
|
if (_isCreation)
|
||||||
{
|
{
|
||||||
message.kind = EVMC_CREATE;
|
message.kind = EVMC_CREATE;
|
||||||
message.destination = EVMHost::convertToEVMC(h160{});
|
message.recipient = {};
|
||||||
|
message.code_address = {};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
message.kind = EVMC_CALL;
|
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>();
|
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);
|
m_output = bytes(result.output_data, result.output_data + result.output_size);
|
||||||
if (_isCreation)
|
if (_isCreation)
|
||||||
@ -210,11 +213,12 @@ void ExecutionFramework::sendEther(h160 const& _addr, u256 const& _amount)
|
|||||||
if (_amount > 0)
|
if (_amount > 0)
|
||||||
cout << " value: " << _amount << endl;
|
cout << " value: " << _amount << endl;
|
||||||
}
|
}
|
||||||
evmc_message message = {};
|
evmc_message message{};
|
||||||
message.sender = EVMHost::convertToEVMC(m_sender);
|
message.sender = EVMHost::convertToEVMC(m_sender);
|
||||||
message.value = EVMHost::convertToEVMC(_amount);
|
message.value = EVMHost::convertToEVMC(_amount);
|
||||||
message.kind = EVMC_CALL;
|
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>();
|
message.gas = InitialGas.convert_to<int64_t>();
|
||||||
|
|
||||||
m_evmcHost->call(message);
|
m_evmcHost->call(message);
|
||||||
|
@ -272,7 +272,6 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
u256 const GasPrice = 10 * gwei;
|
|
||||||
u256 const InitialGas = 100000000;
|
u256 const InitialGas = 100000000;
|
||||||
|
|
||||||
void selectVM(evmc_capabilities _cap = evmc_capabilities::EVMC_CAPABILITY_EVM1);
|
void selectVM(evmc_capabilities _cap = evmc_capabilities::EVMC_CAPABILITY_EVM1);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# EVMC
|
# 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.
|
Important: The `MockedAccount.storage` is changed to a map from unordered_map as ordering is important for fuzzing.
|
||||||
|
217
test/evmc/evmc.h
217
test/evmc/evmc.h
@ -2,7 +2,7 @@
|
|||||||
* EVMC: Ethereum Client-VM Connector API
|
* EVMC: Ethereum Client-VM Connector API
|
||||||
*
|
*
|
||||||
* @copyright
|
* @copyright
|
||||||
* Copyright 2016-2019 The EVMC Authors.
|
* Copyright 2016 The EVMC Authors.
|
||||||
* Licensed under the Apache License, Version 2.0.
|
* Licensed under the Apache License, Version 2.0.
|
||||||
*
|
*
|
||||||
* @defgroup EVMC EVMC
|
* @defgroup EVMC EVMC
|
||||||
@ -44,7 +44,7 @@ enum
|
|||||||
*
|
*
|
||||||
* @see @ref versioning
|
* @see @ref versioning
|
||||||
*/
|
*/
|
||||||
EVMC_ABI_VERSION = 9
|
EVMC_ABI_VERSION = 10
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -89,8 +89,9 @@ enum evmc_flags
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The message describing an EVM call,
|
* The message describing an EVM call, including a zero-depth calls from a transaction origin.
|
||||||
* 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
|
struct evmc_message
|
||||||
{
|
{
|
||||||
@ -103,21 +104,48 @@ struct evmc_message
|
|||||||
*/
|
*/
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
|
||||||
/** The call depth. */
|
/**
|
||||||
|
* The present depth of the message call stack.
|
||||||
|
*
|
||||||
|
* Defined as `e` in the Yellow Paper.
|
||||||
|
*/
|
||||||
int32_t depth;
|
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;
|
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;
|
evmc_address sender;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The message input data.
|
* 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.
|
* This MAY be NULL.
|
||||||
*/
|
*/
|
||||||
const uint8_t* input_data;
|
const uint8_t* input_data;
|
||||||
@ -131,30 +159,49 @@ struct evmc_message
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount of Ether transferred with the 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;
|
evmc_uint256be value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The optional value used in new contract address construction.
|
* 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;
|
evmc_bytes32 create2_salt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The address of the code to be executed.
|
||||||
|
*
|
||||||
|
* For ::EVMC_CALLCODE or ::EVMC_DELEGATECALL this may be different from
|
||||||
|
* the evmc_message::recipient.
|
||||||
|
* Not required when invoking evmc_execute_fn(), only when invoking evmc_call_fn().
|
||||||
|
* Ignored if kind is ::EVMC_CREATE or ::EVMC_CREATE2.
|
||||||
|
*
|
||||||
|
* In case of ::EVMC_CAPABILITY_PRECOMPILES implementation, this fields should be inspected
|
||||||
|
* to identify the requested precompile.
|
||||||
|
*
|
||||||
|
* Defined as `c` in the Yellow Paper.
|
||||||
|
*/
|
||||||
|
evmc_address code_address;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/** The transaction and block data for execution. */
|
/** The transaction and block data for execution. */
|
||||||
struct evmc_tx_context
|
struct evmc_tx_context
|
||||||
{
|
{
|
||||||
evmc_uint256be tx_gas_price; /**< The transaction gas price. */
|
evmc_uint256be tx_gas_price; /**< The transaction gas price. */
|
||||||
evmc_address tx_origin; /**< The transaction origin account. */
|
evmc_address tx_origin; /**< The transaction origin account. */
|
||||||
evmc_address block_coinbase; /**< The miner of the block. */
|
evmc_address block_coinbase; /**< The miner of the block. */
|
||||||
int64_t block_number; /**< The block number. */
|
int64_t block_number; /**< The block number. */
|
||||||
int64_t block_timestamp; /**< The block timestamp. */
|
int64_t block_timestamp; /**< The block timestamp. */
|
||||||
int64_t block_gas_limit; /**< The block gas limit. */
|
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 chain_id; /**< The blockchain's ChainID. */
|
||||||
evmc_uint256be block_base_fee; /**< The block base fee per gas (EIP-1559, EIP-3198). */
|
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;
|
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.
|
* The reference to output data.
|
||||||
*
|
*
|
||||||
@ -396,12 +451,11 @@ struct evmc_result
|
|||||||
evmc_release_result_fn release;
|
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:
|
* The create address may be provided even though the contract creation has failed
|
||||||
* - it is a result of the Host method evmc_host_interface::call
|
* (evmc_result::status_code is not ::EVMC_SUCCESS). This is useful in situations
|
||||||
* - and the result describes successful contract creation
|
* when the address is observable, e.g. access to it remains warm.
|
||||||
* (evmc_result::status_code is ::EVMC_SUCCESS).
|
|
||||||
* In all other cases the address MUST be null bytes.
|
* In all other cases the address MUST be null bytes.
|
||||||
*/
|
*/
|
||||||
evmc_address create_address;
|
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.
|
* 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
|
* For the purpose of explaining the meaning of each element, the following
|
||||||
* notation is used:
|
* notation is used:
|
||||||
* - 0 is zero value,
|
* - 0 is zero value,
|
||||||
* - X != 0 (X is any value other than 0),
|
* - X != 0 (X is any value other than 0),
|
||||||
* - Y != X, Y != 0 (Y is any value other than X and 0),
|
* - Y != 0, Y != X, (Y is any value other than X and 0),
|
||||||
* - Z != Y (Z is any value other than Y),
|
* - Z != 0, Z != X, Z != X (Z is any value other than Y and X and 0),
|
||||||
* - the "->" means the change from one value to another.
|
* - 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
|
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.
|
* 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
|
* 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
|
* 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 context The pointer to the Host execution context.
|
||||||
* @param address The address of the account.
|
* @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 context The pointer to the Host execution context. See ::evmc_host_context.
|
||||||
* @param address The address of the contract to be selfdestructed.
|
* @param address The address of the contract to be selfdestructed.
|
||||||
* @param beneficiary The address where the remaining ETH is going to be transferred.
|
* @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* address,
|
||||||
const evmc_address* beneficiary);
|
const evmc_address* beneficiary);
|
||||||
|
|
||||||
@ -821,26 +934,40 @@ enum evmc_revision
|
|||||||
/**
|
/**
|
||||||
* The Berlin 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,
|
EVMC_BERLIN = 8,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The London revision.
|
* 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,
|
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.
|
* 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. */
|
/** The maximum EVM revision supported. */
|
||||||
EVMC_MAX_REVISION = EVMC_SHANGHAI,
|
EVMC_MAX_REVISION = EVMC_CANCUN,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The latest known EVM revision with finalized specification.
|
* The latest known EVM revision with finalized specification.
|
||||||
@ -894,7 +1021,7 @@ enum evmc_capabilities
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The VM is capable of executing the precompiled contracts
|
* 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 EIP-1352 (https://eips.ethereum.org/EIPS/eip-1352) specifies
|
||||||
* the range 0x000...0000 - 0x000...ffff of addresses
|
* the range 0x000...0000 - 0x000...ffff of addresses
|
||||||
|
@ -1,20 +1,28 @@
|
|||||||
/* EVMC: Ethereum Client-VM Connector API.
|
// EVMC: Ethereum Client-VM Connector API.
|
||||||
* Copyright 2018-2020 The EVMC Authors.
|
// Copyright 2018 The EVMC Authors.
|
||||||
* Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||||
*/
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <evmc/evmc.h>
|
#include <evmc/evmc.h>
|
||||||
#include <evmc/helpers.h>
|
#include <evmc/helpers.h>
|
||||||
|
#include <evmc/hex.hpp>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string_view>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
static_assert(EVMC_LATEST_STABLE_REVISION <= EVMC_MAX_REVISION,
|
||||||
|
"latest stable revision ill-defined");
|
||||||
|
|
||||||
/// EVMC C++ API - wrappers and bindings for C++
|
/// EVMC C++ API - wrappers and bindings for C++
|
||||||
/// @ingroup cpp
|
/// @ingroup cpp
|
||||||
namespace evmc
|
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.
|
/// 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.
|
/// 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.
|
/// Explicit operator converting to bool.
|
||||||
inline constexpr 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 fixed size array of 32 bytes for storing 256-bit EVM values.
|
/// 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.
|
/// 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.
|
/// 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 literals
|
||||||
{
|
{
|
||||||
namespace internal
|
/// Converts a raw literal into value of type T.
|
||||||
{
|
///
|
||||||
constexpr int from_hex(char c) noexcept
|
/// This function is expected to be used on literals in constexpr context only.
|
||||||
{
|
/// In case the input is invalid the std::terminate() is called.
|
||||||
return (c >= 'a' && c <= 'f') ? c - ('a' - 10) :
|
/// TODO(c++20): Use consteval.
|
||||||
(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]));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T from_hex(const char*) noexcept;
|
constexpr T parse(std::string_view s) noexcept
|
||||||
|
|
||||||
template <>
|
|
||||||
constexpr bytes32 from_hex<bytes32>(const char* s) noexcept
|
|
||||||
{
|
{
|
||||||
return {
|
return from_hex<T>(s).value();
|
||||||
{{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)}}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
/// Literal for evmc::address.
|
||||||
template <char... c>
|
constexpr address operator""_address(const char* s) noexcept
|
||||||
constexpr address operator""_address() noexcept
|
|
||||||
{
|
{
|
||||||
return internal::from_literal<address, c...>();
|
return parse<address>(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Literal for evmc::bytes32.
|
/// Literal for evmc::bytes32.
|
||||||
template <char... c>
|
constexpr bytes32 operator""_bytes32(const char* s) noexcept
|
||||||
constexpr bytes32 operator""_bytes32() noexcept
|
|
||||||
{
|
{
|
||||||
return internal::from_literal<bytes32, c...>();
|
return parse<bytes32>(s);
|
||||||
}
|
}
|
||||||
} // namespace literals
|
} // namespace literals
|
||||||
|
|
||||||
using 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().
|
/// Alias for evmc_make_result().
|
||||||
constexpr auto make_result = 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
|
/// This is a RAII wrapper for evmc_result and objects of this type
|
||||||
/// automatically release attached resources.
|
/// automatically release attached resources.
|
||||||
class result : private evmc_result
|
class Result : private evmc_result
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using evmc_result::create_address;
|
using evmc_result::create_address;
|
||||||
using evmc_result::gas_left;
|
using evmc_result::gas_left;
|
||||||
|
using evmc_result::gas_refund;
|
||||||
using evmc_result::output_data;
|
using evmc_result::output_data;
|
||||||
using evmc_result::output_size;
|
using evmc_result::output_size;
|
||||||
using evmc_result::status_code;
|
using evmc_result::status_code;
|
||||||
@ -359,40 +345,70 @@ public:
|
|||||||
///
|
///
|
||||||
/// @param _status_code The status code.
|
/// @param _status_code The status code.
|
||||||
/// @param _gas_left The amount of gas left.
|
/// @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_data The pointer to the output.
|
||||||
/// @param _output_size The output size.
|
/// @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_left,
|
||||||
const uint8_t* _output_data,
|
int64_t _gas_refund,
|
||||||
size_t _output_size) noexcept
|
const uint8_t* _output_data,
|
||||||
: evmc_result{make_result(_status_code, _gas_left, _output_data, _output_size)}
|
size_t _output_size) noexcept
|
||||||
|
: evmc_result{make_result(_status_code, _gas_left, _gas_refund, _output_data, _output_size)}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
/// Creates the result without output.
|
||||||
|
///
|
||||||
|
/// @param _status_code The status code.
|
||||||
|
/// @param _gas_left The amount of gas left.
|
||||||
|
/// @param _gas_refund The amount of refunded gas.
|
||||||
|
explicit Result(evmc_status_code _status_code = EVMC_INTERNAL_ERROR,
|
||||||
|
int64_t _gas_left = 0,
|
||||||
|
int64_t _gas_refund = 0) noexcept
|
||||||
|
: evmc_result{make_result(_status_code, _gas_left, _gas_refund, nullptr, 0)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// Creates the result of contract creation.
|
||||||
|
///
|
||||||
|
/// @param _status_code The status code.
|
||||||
|
/// @param _gas_left The amount of gas left.
|
||||||
|
/// @param _gas_refund The amount of refunded gas.
|
||||||
|
/// @param _create_address The address of the possibly created account.
|
||||||
|
explicit Result(evmc_status_code _status_code,
|
||||||
|
int64_t _gas_left,
|
||||||
|
int64_t _gas_refund,
|
||||||
|
const evmc_address& _create_address) noexcept
|
||||||
|
: evmc_result{make_result(_status_code, _gas_left, _gas_refund, nullptr, 0)}
|
||||||
|
{
|
||||||
|
create_address = _create_address;
|
||||||
|
}
|
||||||
|
|
||||||
/// Converting constructor from raw evmc_result.
|
/// 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.
|
/// Destructor responsible for automatically releasing attached resources.
|
||||||
~result() noexcept
|
~Result() noexcept
|
||||||
{
|
{
|
||||||
if (release != nullptr)
|
if (release != nullptr)
|
||||||
release(this);
|
release(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move constructor.
|
/// 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.
|
other.release = nullptr; // Disable releasing of the rvalue object.
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move assignment operator.
|
/// Move assignment operator.
|
||||||
///
|
///
|
||||||
/// The self-assigment MUST never happen.
|
/// The self-assignment MUST never happen.
|
||||||
///
|
///
|
||||||
/// @param other The other result object.
|
/// @param other The other result object.
|
||||||
/// @return The reference to the left-hand side 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.
|
static_cast<evmc_result&>(*this) = other; // Copy data.
|
||||||
other.release = nullptr; // Disable releasing of the rvalue object.
|
other.release = nullptr; // Disable releasing of the rvalue object.
|
||||||
return *this;
|
return *this;
|
||||||
@ -448,10 +464,10 @@ public:
|
|||||||
size_t buffer_size) const noexcept = 0;
|
size_t buffer_size) const noexcept = 0;
|
||||||
|
|
||||||
/// @copydoc evmc_host_interface::selfdestruct
|
/// @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
|
/// @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
|
/// @copydoc evmc_host_interface::get_tx_context
|
||||||
virtual evmc_tx_context get_tx_context() const noexcept = 0;
|
virtual evmc_tx_context get_tx_context() const noexcept = 0;
|
||||||
@ -481,7 +497,6 @@ class HostContext : public HostInterface
|
|||||||
{
|
{
|
||||||
const evmc_host_interface* host = nullptr;
|
const evmc_host_interface* host = nullptr;
|
||||||
evmc_host_context* context = nullptr;
|
evmc_host_context* context = nullptr;
|
||||||
mutable evmc_tx_context tx_context = {};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Default constructor for null Host context.
|
/// Default constructor for null Host context.
|
||||||
@ -534,28 +549,18 @@ public:
|
|||||||
return host->copy_code(context, &address, code_offset, buffer_data, buffer_size);
|
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()
|
/// @copydoc HostInterface::get_tx_context()
|
||||||
///
|
evmc_tx_context get_tx_context() const noexcept final { return host->get_tx_context(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes32 get_block_hash(int64_t number) const noexcept final
|
bytes32 get_block_hash(int64_t number) const noexcept final
|
||||||
{
|
{
|
||||||
@ -685,18 +690,18 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// @copydoc evmc_execute()
|
/// @copydoc evmc_execute()
|
||||||
result execute(const evmc_host_interface& host,
|
Result execute(const evmc_host_interface& host,
|
||||||
evmc_host_context* ctx,
|
evmc_host_context* ctx,
|
||||||
evmc_revision rev,
|
evmc_revision rev,
|
||||||
const evmc_message& msg,
|
const evmc_message& msg,
|
||||||
const uint8_t* code,
|
const uint8_t* code,
|
||||||
size_t code_size) noexcept
|
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.
|
/// Convenient variant of the VM::execute() that takes reference to evmc::Host class.
|
||||||
result execute(Host& host,
|
Result execute(Host& host,
|
||||||
evmc_revision rev,
|
evmc_revision rev,
|
||||||
const evmc_message& msg,
|
const evmc_message& msg,
|
||||||
const uint8_t* code,
|
const uint8_t* code,
|
||||||
@ -713,12 +718,12 @@ public:
|
|||||||
/// but without providing the Host context and interface.
|
/// but without providing the Host context and interface.
|
||||||
/// This method is for experimental precompiles support where execution is
|
/// This method is for experimental precompiles support where execution is
|
||||||
/// guaranteed not to require any Host access.
|
/// guaranteed not to require any Host access.
|
||||||
result execute(evmc_revision rev,
|
Result execute(evmc_revision rev,
|
||||||
const evmc_message& msg,
|
const evmc_message& msg,
|
||||||
const uint8_t* code,
|
const uint8_t* code,
|
||||||
size_t code_size) noexcept
|
size_t code_size) noexcept
|
||||||
{
|
{
|
||||||
return result{
|
return Result{
|
||||||
m_instance->execute(m_instance, nullptr, nullptr, rev, &msg, code, code_size)};
|
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);
|
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* addr,
|
||||||
const evmc_address* beneficiary) noexcept
|
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
|
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
|
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::account_exists, ::evmc::internal::get_storage,
|
||||||
::evmc::internal::set_storage, ::evmc::internal::get_balance,
|
::evmc::internal::set_storage, ::evmc::internal::get_balance,
|
||||||
::evmc::internal::get_code_size, ::evmc::internal::get_code_hash,
|
::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
|
} // 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
|
namespace std
|
||||||
{
|
{
|
||||||
/// Hash operator template specialization for evmc::address. Needed for unordered containers.
|
/// 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.
|
// EVMC: Ethereum Client-VM Connector API.
|
||||||
* Copyright 2018-2019 The EVMC Authors.
|
// Copyright 2018 The EVMC Authors.
|
||||||
* Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EVMC Helpers
|
* EVMC Helpers
|
||||||
@ -10,8 +9,6 @@
|
|||||||
* These are convenient for languages where invoking function pointers
|
* These are convenient for languages where invoking function pointers
|
||||||
* is "ugly" or impossible (such as Go).
|
* is "ugly" or impossible (such as Go).
|
||||||
*
|
*
|
||||||
* It also contains helpers (overloaded operators) for using EVMC types effectively in C++.
|
|
||||||
*
|
|
||||||
* @defgroup helpers EVMC Helpers
|
* @defgroup helpers EVMC Helpers
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
@ -21,6 +18,12 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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.
|
* 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 status_code The status code.
|
||||||
/// @param gas_left The amount of gas left.
|
/// @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_data The pointer to the output.
|
||||||
/// @param output_size The output size.
|
/// @param output_size The output size.
|
||||||
static inline struct evmc_result evmc_make_result(enum evmc_status_code status_code,
|
static inline struct evmc_result evmc_make_result(enum evmc_status_code status_code,
|
||||||
int64_t gas_left,
|
int64_t gas_left,
|
||||||
|
int64_t gas_refund,
|
||||||
const uint8_t* output_data,
|
const uint8_t* output_data,
|
||||||
size_t output_size)
|
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.status_code = status_code;
|
||||||
result.gas_left = gas_left;
|
result.gas_left = gas_left;
|
||||||
|
result.gas_refund = gas_refund;
|
||||||
return result;
|
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.
|
// EVMC: Ethereum Client-VM Connector API.
|
||||||
* Copyright 2018-2019 The EVMC Authors.
|
// Copyright 2018 The EVMC Authors.
|
||||||
* Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||||
*/
|
|
||||||
|
|
||||||
#include <evmc/loader.h>
|
#include <evmc/loader.h>
|
||||||
|
|
||||||
@ -27,6 +26,7 @@
|
|||||||
#define DLL_HANDLE void*
|
#define DLL_HANDLE void*
|
||||||
#define DLL_OPEN(filename) dlopen(filename, RTLD_LAZY)
|
#define DLL_OPEN(filename) dlopen(filename, RTLD_LAZY)
|
||||||
#define DLL_CLOSE(handle) dlclose(handle)
|
#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_CREATE_FN(handle, name) (evmc_create_fn)(uintptr_t) dlsym(handle, name)
|
||||||
#define DLL_GET_ERROR_MSG() dlerror()
|
#define DLL_GET_ERROR_MSG() dlerror()
|
||||||
#endif
|
#endif
|
||||||
@ -60,17 +60,20 @@ static
|
|||||||
dest[0] = 0;
|
dest[0] = 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
|
||||||
memcpy(dest, src, len);
|
memcpy(dest, src, len);
|
||||||
dest[len] = 0;
|
dest[len] = 0;
|
||||||
return 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;
|
static const char* last_error_msg = NULL;
|
||||||
|
|
||||||
#define LAST_ERROR_MSG_BUFFER_SIZE 511
|
|
||||||
|
|
||||||
// Buffer for formatted error messages.
|
// Buffer for formatted error messages.
|
||||||
// It has one null byte extra to avoid buffer read overflow during concurrent access.
|
// 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];
|
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_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
|
||||||
if (vsnprintf(last_error_msg_buffer, LAST_ERROR_MSG_BUFFER_SIZE, format, args) <
|
if (vsnprintf(last_error_msg_buffer, LAST_ERROR_MSG_BUFFER_SIZE, format, args) <
|
||||||
LAST_ERROR_MSG_BUFFER_SIZE)
|
LAST_ERROR_MSG_BUFFER_SIZE)
|
||||||
last_error_msg = last_error_msg_buffer;
|
last_error_msg = last_error_msg_buffer;
|
||||||
@ -181,7 +185,7 @@ exit:
|
|||||||
return create_fn;
|
return create_fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* evmc_last_error_msg()
|
const char* evmc_last_error_msg(void)
|
||||||
{
|
{
|
||||||
const char* m = last_error_msg;
|
const char* m = last_error_msg;
|
||||||
last_error_msg = NULL;
|
last_error_msg = NULL;
|
||||||
@ -267,16 +271,15 @@ struct evmc_vm* evmc_load_and_configure(const char* config, enum evmc_loader_err
|
|||||||
if (!vm)
|
if (!vm)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (vm->set_option == NULL && strlen(options) != 0)
|
|
||||||
{
|
|
||||||
ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s) does not support any options",
|
|
||||||
vm->name, path);
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
while (strlen(options) != 0)
|
while (strlen(options) != 0)
|
||||||
{
|
{
|
||||||
|
if (vm->set_option == NULL)
|
||||||
|
{
|
||||||
|
ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s) does not support any options",
|
||||||
|
vm->name, path);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
char* option = get_token(&options, ',');
|
char* option = get_token(&options, ',');
|
||||||
|
|
||||||
// Slit option into name and value by taking the name token.
|
// Slit option into name and value by taking the name token.
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
/* EVMC: Ethereum Client-VM Connector API.
|
// EVMC: Ethereum Client-VM Connector API.
|
||||||
* Copyright 2018-2019 The EVMC Authors.
|
// Copyright 2018 The EVMC Authors.
|
||||||
* Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EVMC Loader Library
|
* EVMC Loader Library
|
||||||
@ -21,7 +20,10 @@ extern "C" {
|
|||||||
/** The function pointer type for EVMC create functions. */
|
/** The function pointer type for EVMC create functions. */
|
||||||
typedef struct evmc_vm* (*evmc_create_fn)(void);
|
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
|
enum evmc_loader_error_code
|
||||||
{
|
{
|
||||||
/** The loader succeeded. */
|
/** The loader succeeded. */
|
||||||
@ -46,7 +48,11 @@ enum evmc_loader_error_code
|
|||||||
EVMC_LOADER_INVALID_OPTION_NAME = 6,
|
EVMC_LOADER_INVALID_OPTION_NAME = 6,
|
||||||
|
|
||||||
/** The VM option value is invalid. */
|
/** 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 <evmc/evmc.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@ -16,7 +17,7 @@ namespace evmc
|
|||||||
using bytes = std::basic_string<uint8_t>;
|
using bytes = std::basic_string<uint8_t>;
|
||||||
|
|
||||||
/// Extended value (with original value and access flag) for account storage.
|
/// Extended value (with original value and access flag) for account storage.
|
||||||
struct storage_value
|
struct StorageValue
|
||||||
{
|
{
|
||||||
/// The current storage value.
|
/// The current storage value.
|
||||||
bytes32 current;
|
bytes32 current;
|
||||||
@ -28,7 +29,20 @@ struct storage_value
|
|||||||
evmc_access_status access_status = EVMC_ACCESS_COLD;
|
evmc_access_status access_status = EVMC_ACCESS_COLD;
|
||||||
|
|
||||||
/// Default constructor.
|
/// 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.
|
/// Mocked account.
|
||||||
@ -47,7 +61,7 @@ struct MockedAccount
|
|||||||
uint256be balance;
|
uint256be balance;
|
||||||
|
|
||||||
/// The account storage map.
|
/// The account storage map.
|
||||||
std::map<bytes32, storage_value> storage;
|
std::map<bytes32, StorageValue> storage;
|
||||||
|
|
||||||
/// Helper method for setting balance by numeric type.
|
/// Helper method for setting balance by numeric type.
|
||||||
void set_balance(uint64_t x) noexcept
|
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.
|
/// The set of all accounts in the Host, organized by their addresses.
|
||||||
std::unordered_map<address, MockedAccount> accounts;
|
std::unordered_map<address, MockedAccount> accounts;
|
||||||
|
|
||||||
@ -129,8 +127,9 @@ public:
|
|||||||
/// The record of all LOGs passed to the emit_log() method.
|
/// The record of all LOGs passed to the emit_log() method.
|
||||||
std::vector<log_record> recorded_logs;
|
std::vector<log_record> recorded_logs;
|
||||||
|
|
||||||
/// The record of all SELFDESTRUCTs from the selfdestruct() method.
|
/// The record of all SELFDESTRUCTs from the selfdestruct() method
|
||||||
std::vector<selfdestruct_record> recorded_selfdestructs;
|
/// as a map selfdestructed_address => [beneficiary1, beneficiary2, ...].
|
||||||
|
std::unordered_map<address, std::vector<address>> recorded_selfdestructs;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// The copy of call inputs for the recorded_calls record.
|
/// The copy of call inputs for the recorded_calls record.
|
||||||
@ -176,32 +175,142 @@ public:
|
|||||||
const bytes32& value) noexcept override
|
const bytes32& value) noexcept override
|
||||||
{
|
{
|
||||||
record_account_access(addr);
|
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.
|
// Follow the EIP-2200 specification as closely as possible.
|
||||||
// WARNING! This is not complete implementation as refund is not handled here.
|
// 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)
|
// 2. "If current value equals new value (this is a no-op)"
|
||||||
return EVMC_STORAGE_UNCHANGED;
|
if (current == value)
|
||||||
|
{
|
||||||
evmc_storage_status status{};
|
// "SLOAD_GAS is deducted"
|
||||||
if (old.original == old.current) // Storage slot not dirty
|
return EVMC_STORAGE_ASSIGNED;
|
||||||
{
|
}
|
||||||
if (!old.current)
|
// 3. "If current value does not equal new value"
|
||||||
status = EVMC_STORAGE_ADDED;
|
|
||||||
else if (value)
|
|
||||||
status = EVMC_STORAGE_MODIFIED;
|
|
||||||
else
|
else
|
||||||
status = EVMC_STORAGE_DELETED;
|
{
|
||||||
}
|
// 3.1. "If original value equals current value
|
||||||
else
|
// (this storage slot has not been changed by the current execution context)"
|
||||||
status = EVMC_STORAGE_MODIFIED_AGAIN;
|
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;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,16 +369,18 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Selfdestruct the account (EVMC host method).
|
/// 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);
|
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).
|
/// 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())
|
if (recorded_calls.empty())
|
||||||
{
|
{
|
||||||
@ -288,7 +399,7 @@ public:
|
|||||||
call_msg.input_data = input_copy.data();
|
call_msg.input_data = input_copy.data();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result{call_result};
|
return Result{call_result};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get transaction context (EVMC host method).
|
/// 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
|
/// 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.
|
/// access in MockedHost::recorded_account_accesses and return previous access status.
|
||||||
/// This methods returns ::EVMC_ACCESS_WARM for known addresses of precompiles.
|
/// 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:
|
/// ::EVMC_ACCESS_WARM. Therefore, you should init the MockedHost with:
|
||||||
///
|
///
|
||||||
/// mocked_host.access_account(msg.sender);
|
/// 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.
|
/// 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.
|
/// 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
|
/// This method is required by EIP-2929 introduced in ::EVMC_BERLIN. In records
|
||||||
/// account's storage key has been access and returns the previous access status.
|
/// that the given account's storage key has been access and returns the
|
||||||
/// To mock storage access list (EIP-2930), you can pre-init account's storage values with
|
/// previous access status. To mock storage access list (EIP-2930), you can
|
||||||
/// the ::EVMC_ACCESS_WARM flag:
|
/// 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 addr The account address.
|
||||||
/// @param key The account's storage key.
|
/// @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.
|
/// the ::EVMC_ACCESS_COLD otherwise.
|
||||||
evmc_access_status access_storage(const address& addr, const bytes32& key) noexcept override
|
evmc_access_status access_storage(const address& addr, const bytes32& key) noexcept override
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
/* EVMC: Ethereum Client-VM Connector API.
|
// EVMC: Ethereum Client-VM Connector API.
|
||||||
* Copyright 2018-2019 The EVMC Authors.
|
// Copyright 2018 The EVMC Authors.
|
||||||
* Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
@ -83,31 +83,32 @@ evmc_message EvmoneUtility::initializeMessage(bytes const& _input)
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
evmc::result EvmoneUtility::executeContract(
|
evmc::Result EvmoneUtility::executeContract(
|
||||||
bytes const& _functionHash,
|
bytes const& _functionHash,
|
||||||
evmc_address _deployedAddress
|
evmc_address _deployedAddress
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
evmc_message message = initializeMessage(_functionHash);
|
evmc_message message = initializeMessage(_functionHash);
|
||||||
message.destination = _deployedAddress;
|
message.recipient = _deployedAddress;
|
||||||
|
message.code_address = _deployedAddress;
|
||||||
message.kind = EVMC_CALL;
|
message.kind = EVMC_CALL;
|
||||||
return m_evmHost.call(message);
|
return m_evmHost.call(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
evmc::result EvmoneUtility::deployContract(bytes const& _code)
|
evmc::Result EvmoneUtility::deployContract(bytes const& _code)
|
||||||
{
|
{
|
||||||
evmc_message message = initializeMessage(_code);
|
evmc_message message = initializeMessage(_code);
|
||||||
message.kind = EVMC_CREATE;
|
message.kind = EVMC_CREATE;
|
||||||
return m_evmHost.call(message);
|
return m_evmHost.call(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
evmc::result EvmoneUtility::deployAndExecute(
|
evmc::Result EvmoneUtility::deployAndExecute(
|
||||||
bytes const& _byteCode,
|
bytes const& _byteCode,
|
||||||
string const& _hexEncodedInput
|
string const& _hexEncodedInput
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Deploy contract and signal failure if deploy failed
|
// Deploy contract and signal failure if deploy failed
|
||||||
evmc::result createResult = deployContract(_byteCode);
|
evmc::Result createResult = deployContract(_byteCode);
|
||||||
solAssert(
|
solAssert(
|
||||||
createResult.status_code == EVMC_SUCCESS,
|
createResult.status_code == EVMC_SUCCESS,
|
||||||
"SolidityEvmoneInterface: Contract creation failed"
|
"SolidityEvmoneInterface: Contract creation failed"
|
||||||
@ -115,7 +116,7 @@ evmc::result EvmoneUtility::deployAndExecute(
|
|||||||
|
|
||||||
// Execute test function and signal failure if EVM reverted or
|
// Execute test function and signal failure if EVM reverted or
|
||||||
// did not return expected output on successful execution.
|
// did not return expected output on successful execution.
|
||||||
evmc::result callResult = executeContract(
|
evmc::Result callResult = executeContract(
|
||||||
util::fromHex(_hexEncodedInput),
|
util::fromHex(_hexEncodedInput),
|
||||||
createResult.create_address
|
createResult.create_address
|
||||||
);
|
);
|
||||||
@ -128,7 +129,7 @@ evmc::result EvmoneUtility::deployAndExecute(
|
|||||||
return callResult;
|
return callResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
evmc::result EvmoneUtility::compileDeployAndExecute(string _fuzzIsabelle)
|
evmc::Result EvmoneUtility::compileDeployAndExecute(string _fuzzIsabelle)
|
||||||
{
|
{
|
||||||
map<string, h160> libraryAddressMap;
|
map<string, h160> libraryAddressMap;
|
||||||
// Stage 1: Compile and deploy library if present.
|
// 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");
|
solAssert(compilationOutput.has_value(), "Compiling library failed");
|
||||||
CompilerOutput cOutput = compilationOutput.value();
|
CompilerOutput cOutput = compilationOutput.value();
|
||||||
// Deploy contract and signal failure if deploy failed
|
// Deploy contract and signal failure if deploy failed
|
||||||
evmc::result createResult = deployContract(cOutput.byteCode);
|
evmc::Result createResult = deployContract(cOutput.byteCode);
|
||||||
solAssert(
|
solAssert(
|
||||||
createResult.status_code == EVMC_SUCCESS,
|
createResult.status_code == EVMC_SUCCESS,
|
||||||
"SolidityEvmoneInterface: Library deployment failed"
|
"SolidityEvmoneInterface: Library deployment failed"
|
||||||
|
@ -122,7 +122,7 @@ public:
|
|||||||
/// and executing test configuration.
|
/// and executing test configuration.
|
||||||
/// @param _isabelleData contains encoding data to be passed to the
|
/// @param _isabelleData contains encoding data to be passed to the
|
||||||
/// isabelle test entry point.
|
/// 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
|
/// Compares the contents of the memory address pointed to
|
||||||
/// by `_result` of `_length` bytes to u256 zero.
|
/// by `_result` of `_length` bytes to u256 zero.
|
||||||
/// @returns true if `_result` is zero, false
|
/// @returns true if `_result` is zero, false
|
||||||
@ -138,17 +138,17 @@ private:
|
|||||||
/// @returns the result of the execution of the function whose
|
/// @returns the result of the execution of the function whose
|
||||||
/// keccak256 hash is @param _functionHash that is deployed at
|
/// keccak256 hash is @param _functionHash that is deployed at
|
||||||
/// @param _deployedAddress in @param _hostContext.
|
/// @param _deployedAddress in @param _hostContext.
|
||||||
evmc::result executeContract(
|
evmc::Result executeContract(
|
||||||
bytes const& _functionHash,
|
bytes const& _functionHash,
|
||||||
evmc_address _deployedAddress
|
evmc_address _deployedAddress
|
||||||
);
|
);
|
||||||
/// @returns the result of deployment of @param _code on @param _hostContext.
|
/// @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
|
/// Deploys and executes EVM byte code in @param _byteCode on
|
||||||
/// EVM Host referenced by @param _hostContext. Input passed
|
/// EVM Host referenced by @param _hostContext. Input passed
|
||||||
/// to execution context is @param _hexEncodedInput.
|
/// to execution context is @param _hexEncodedInput.
|
||||||
/// @returns result returning by @param _hostContext.
|
/// @returns result returning by @param _hostContext.
|
||||||
evmc::result deployAndExecute(
|
evmc::Result deployAndExecute(
|
||||||
bytes const& _byteCode,
|
bytes const& _byteCode,
|
||||||
std::string const& _hexEncodedInput
|
std::string const& _hexEncodedInput
|
||||||
);
|
);
|
||||||
|
@ -87,11 +87,11 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
evmc::result deployResult = YulEvmoneUtility{}.deployCode(unoptimisedByteCode, hostContext);
|
evmc::Result deployResult = YulEvmoneUtility{}.deployCode(unoptimisedByteCode, hostContext);
|
||||||
if (deployResult.status_code != EVMC_SUCCESS)
|
if (deployResult.status_code != EVMC_SUCCESS)
|
||||||
return;
|
return;
|
||||||
auto callMessage = YulEvmoneUtility{}.callMessage(deployResult.create_address);
|
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
|
// If the fuzzer synthesized input does not contain the revert opcode which
|
||||||
// we lazily check by string find, the EVM call should not revert.
|
// we lazily check by string find, the EVM call should not revert.
|
||||||
bool noRevertInSource = yul_source.find("revert") == string::npos;
|
bool noRevertInSource = yul_source.find("revert") == string::npos;
|
||||||
@ -132,13 +132,13 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
|||||||
|
|
||||||
// Reset host before running optimised code.
|
// Reset host before running optimised code.
|
||||||
hostContext.reset();
|
hostContext.reset();
|
||||||
evmc::result deployResultOpt = YulEvmoneUtility{}.deployCode(optimisedByteCode, hostContext);
|
evmc::Result deployResultOpt = YulEvmoneUtility{}.deployCode(optimisedByteCode, hostContext);
|
||||||
solAssert(
|
solAssert(
|
||||||
deployResultOpt.status_code == EVMC_SUCCESS,
|
deployResultOpt.status_code == EVMC_SUCCESS,
|
||||||
"Evmone: Optimized contract creation failed"
|
"Evmone: Optimized contract creation failed"
|
||||||
);
|
);
|
||||||
auto callMessageOpt = YulEvmoneUtility{}.callMessage(deployResultOpt.create_address);
|
auto callMessageOpt = YulEvmoneUtility{}.callMessage(deployResultOpt.create_address);
|
||||||
evmc::result callResultOpt = hostContext.call(callMessageOpt);
|
evmc::Result callResultOpt = hostContext.call(callMessageOpt);
|
||||||
if (noRevertInSource)
|
if (noRevertInSource)
|
||||||
solAssert(
|
solAssert(
|
||||||
callResultOpt.status_code != EVMC_REVERT,
|
callResultOpt.status_code != EVMC_REVERT,
|
||||||
|
@ -38,7 +38,7 @@ bytes YulAssembler::assemble()
|
|||||||
return m_stack.assemble(YulStack::Machine::EVM).bytecode->bytecode;
|
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
|
// Zero initialize all message fields
|
||||||
evmc_message msg = {};
|
evmc_message msg = {};
|
||||||
@ -74,7 +74,8 @@ evmc_message YulEvmoneUtility::callMessage(evmc_address _address)
|
|||||||
{
|
{
|
||||||
evmc_message call = {};
|
evmc_message call = {};
|
||||||
call.gas = std::numeric_limits<int64_t>::max();
|
call.gas = std::numeric_limits<int64_t>::max();
|
||||||
call.destination = _address;
|
call.recipient = _address;
|
||||||
|
call.code_address = _address;
|
||||||
call.kind = EVMC_CALL;
|
call.kind = EVMC_CALL;
|
||||||
return call;
|
return call;
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ private:
|
|||||||
struct YulEvmoneUtility
|
struct YulEvmoneUtility
|
||||||
{
|
{
|
||||||
/// @returns the result of deploying bytecode @param _input on @param _host.
|
/// @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.
|
/// @returns call message to be sent to @param _address.
|
||||||
static evmc_message callMessage(evmc_address _address);
|
static evmc_message callMessage(evmc_address _address);
|
||||||
/// @returns true if call result indicates a serious error, false otherwise.
|
/// @returns true if call result indicates a serious error, false otherwise.
|
||||||
|
Loading…
Reference in New Issue
Block a user