Merge pull request #10122 from ethereum/ewasm-interpreter

[ewasm] Fix EwasmBuiltinInterpreter to follow the specs
This commit is contained in:
chriseth 2020-10-28 13:11:44 +01:00 committed by GitHub
commit 7b26c099b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 66 additions and 40 deletions

View File

@ -6,7 +6,7 @@
// Trace:
// Memory dump:
// 0: 0000000000000000000000000000000000000000000000000000000000000001
// 20: 0000000000000000000000000000000000000000000000000000000022222222
// 20: 0000000000000000000000000000000022222222000000000000000000000000
// Storage dump:
// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000022222222
// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000022222222
// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000022222222000000000000000000000000
// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000022222222000000000000000000000000

View File

@ -4,6 +4,6 @@
// ----
// Trace:
// Memory dump:
// 20: 0000000000000000000000005555555500000000000000000000000000000000
// 20: 5555555500000000000000000000000000000000000000000000000000000000
// Storage dump:
// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000005555555500000000000000000000000000000000
// 0000000000000000000000000000000000000000000000000000000000000000: 5555555500000000000000000000000000000000000000000000000000000000

View File

@ -4,6 +4,6 @@
// ----
// Trace:
// Memory dump:
// 20: 0000000000000000000000000000000000000000000000000000000009999999
// 20: 9999990900000000000000000000000000000000000000000000000000000000
// Storage dump:
// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000009999999
// 0000000000000000000000000000000000000000000000000000000000000000: 9999990900000000000000000000000000000000000000000000000000000000

View File

@ -4,6 +4,6 @@
// ----
// Trace:
// Memory dump:
// 20: 000000000000000000000000000000000000000000000000000000000000077b
// 20: 0000000000000000000000000000000000000000000000000000000000000dd6
// Storage dump:
// 0000000000000000000000000000000000000000000000000000000000000000: 000000000000000000000000000000000000000000000000000000000000077b
// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000dd6

View File

@ -4,6 +4,6 @@
// ----
// Trace:
// Memory dump:
// 20: 0000000000000000000000006666666600000000000000000000000000000000
// 20: 6666666600000000000000000000000000000000000000000000000000000000
// Storage dump:
// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000006666666600000000000000000000000000000000
// 0000000000000000000000000000000000000000000000000000000000000000: 6666666600000000000000000000000000000000000000000000000000000000

View File

@ -35,6 +35,7 @@ using namespace solidity;
using namespace solidity::yul;
using namespace solidity::yul::test;
using solidity::util::h160;
using solidity::util::h256;
namespace
@ -297,9 +298,7 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
}
else if (_fun == "getExternalBalance")
{
// TODO this does not read the address, but is consistent with
// EVM interpreter implementation.
// If we take the address into account, this needs to use readAddress.
readAddress(arg[0]);
writeU128(arg[1], m_state.balance);
return 0;
}
@ -309,14 +308,15 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
return 1;
else
{
writeU256(arg[1], 0xaaaaaaaa + u256(arg[0] - m_state.blockNumber - 256));
writeBytes32(arg[1], h256(0xaaaaaaaa + u256(arg[0] - m_state.blockNumber - 256)));
return 0;
}
}
else if (_fun == "call")
{
// TODO read args from memory
// TODO use readAddress to read address.
readAddress(arg[1]);
readU128(arg[2]);
accessMemory(arg[3], arg[4]);
logTrace(evmasm::Instruction::CALL, {});
return arg[0] & 1;
}
@ -335,38 +335,38 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
return m_state.calldata.size();
else if (_fun == "callCode")
{
// TODO read args from memory
// TODO use readAddress to read address.
readAddress(arg[1]);
readU128(arg[2]);
accessMemory(arg[3], arg[4]);
logTrace(evmasm::Instruction::CALLCODE, {});
return arg[0] & 1;
}
else if (_fun == "callDelegate")
{
// TODO read args from memory
// TODO use readAddress to read address.
readAddress(arg[1]);
accessMemory(arg[2], arg[3]);
logTrace(evmasm::Instruction::DELEGATECALL, {});
return arg[0] & 1;
}
else if (_fun == "callStatic")
{
// TODO read args from memory
// TODO use readAddress to read address.
readAddress(arg[1]);
accessMemory(arg[2], arg[3]);
logTrace(evmasm::Instruction::STATICCALL, {});
return arg[0] & 1;
}
else if (_fun == "storageStore")
{
m_state.storage[h256(readU256(arg[0]))] = readU256((arg[1]));
m_state.storage[readBytes32(arg[0])] = readBytes32(arg[1]);
return 0;
}
else if (_fun == "storageLoad")
{
writeU256(arg[1], m_state.storage[h256(readU256(arg[0]))]);
writeBytes32(arg[1], m_state.storage[readBytes32(arg[0])]);
return 0;
}
else if (_fun == "getCaller")
{
// TODO should this only write 20 bytes?
writeAddress(arg[0], m_state.caller);
return 0;
}
@ -393,10 +393,11 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
}
else if (_fun == "create")
{
// TODO access memory
// TODO use writeAddress to store resulting address
readU128(arg[0]);
accessMemory(arg[1], arg[2]);
logTrace(evmasm::Instruction::CREATE, {});
return 0xcccccc + arg[1];
writeAddress(arg[3], h160(h256(0xcccccc + arg[1])));
return 1;
}
else if (_fun == "getBlockDifficulty")
{
@ -405,7 +406,7 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
}
else if (_fun == "externalCodeCopy")
{
// TODO use readAddress to read address.
readAddress(arg[0]);
if (accessMemory(arg[1], arg[3]))
// TODO this way extcodecopy and codecopy do the same thing.
copyZeroExtended(
@ -415,8 +416,8 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
return 0;
}
else if (_fun == "getExternalCodeSize")
// Generate "random" code length. Make sure it fits the page size.
return u256(keccak256(h256(readAddress(arg[0])))) & 0xfff;
// Generate "random" code length.
return uint32_t(u256(keccak256(h256(readAddress(arg[0])))) & 0xfff);
else if (_fun == "getGasLeft")
return 0x99;
else if (_fun == "getBlockGasLimit")
@ -428,9 +429,18 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
}
else if (_fun == "log")
{
accessMemory(arg[0], arg[1]);
uint64_t numberOfTopics = arg[2];
if (numberOfTopics > 4)
throw ExplicitlyTerminated();
if (numberOfTopics > 0)
readBytes32(arg[3]);
if (numberOfTopics > 1)
readBytes32(arg[4]);
if (numberOfTopics > 2)
readBytes32(arg[5]);
if (numberOfTopics > 3)
readBytes32(arg[6]);
logTrace(evmasm::logInstruction(numberOfTopics), {});
return 0;
}
@ -472,7 +482,7 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
}
else if (_fun == "selfDestruct")
{
// TODO use readAddress to read address.
readAddress(arg[0]);
logTrace(evmasm::Instruction::SELFDESTRUCT, {});
throw ExplicitlyTerminated();
}
@ -523,6 +533,12 @@ uint32_t EwasmBuiltinInterpreter::readMemoryHalfWord(uint64_t _offset)
return r;
}
void EwasmBuiltinInterpreter::writeMemory(uint64_t _offset, bytes const& _value)
{
for (size_t i = 0; i < _value.size(); i++)
m_state.memory[_offset + i] = _value[i];
}
void EwasmBuiltinInterpreter::writeMemoryWord(uint64_t _offset, uint64_t _value)
{
for (size_t i = 0; i < 8; i++)
@ -545,7 +561,7 @@ void EwasmBuiltinInterpreter::writeU256(uint64_t _offset, u256 _value, size_t _c
accessMemory(_offset, _croppedTo);
for (size_t i = 0; i < _croppedTo; i++)
{
m_state.memory[_offset + _croppedTo - 1 - i] = uint8_t(_value & 0xff);
m_state.memory[_offset + i] = uint8_t(_value & 0xff);
_value >>= 8;
}
}
@ -553,9 +569,9 @@ void EwasmBuiltinInterpreter::writeU256(uint64_t _offset, u256 _value, size_t _c
u256 EwasmBuiltinInterpreter::readU256(uint64_t _offset, size_t _croppedTo)
{
accessMemory(_offset, _croppedTo);
u256 value;
u256 value{0};
for (size_t i = 0; i < _croppedTo; i++)
value = (value << 8) | m_state.memory[_offset + i];
value = (value << 8) | m_state.memory[_offset + _croppedTo - 1 - i];
return value;
}

View File

@ -24,6 +24,7 @@
#include <libyul/AsmDataForward.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/FixedHash.h>
#include <vector>
@ -61,6 +62,8 @@ struct InterpreterState;
*
* The main focus is that the generated execution trace is the same for equivalent executions
* and likely to be different for non-equivalent executions.
*
* The type names are following the Ewasm specification (https://github.com/ewasm/design/blob/master/eth_interface.md).
*/
class EwasmBuiltinInterpreter
{
@ -99,6 +102,9 @@ private:
/// @returns the memory contents (4 bytes) at the provided address (little-endian).
/// Does not adjust msize, use @a accessMemory for that
uint32_t readMemoryHalfWord(uint64_t _offset);
/// Writes bytes to memory.
/// Does not adjust msize, use @a accessMemory for that
void writeMemory(uint64_t _offset, bytes const& _value);
/// Writes a word to memory (little-endian)
/// Does not adjust msize, use @a accessMemory for that
void writeMemoryWord(uint64_t _offset, uint64_t _value);
@ -109,14 +115,18 @@ private:
/// Does not adjust msize, use @a accessMemory for that
void writeMemoryByte(uint64_t _offset, uint8_t _value);
/// Helper for eth.* builtins. Writes to memory (big-endian) and always returns zero.
/// Helper for eth.* builtins. Writes to memory (little-endian) and always returns zero.
void writeU256(uint64_t _offset, u256 _value, size_t _croppedTo = 32);
void writeU128(uint64_t _offset, u256 _value) { writeU256(_offset, std::move(_value), 16); }
void writeAddress(uint64_t _offset, u256 _value) { writeU256(_offset, std::move(_value), 20); }
/// Helper for eth.* builtins. Reads from memory (big-endian) and returns the value;
/// Helper for eth.* builtins. Writes to memory (as a byte string).
void writeBytes32(uint64_t _offset, util::h256 _value) { accessMemory(_offset, 32); writeMemory(_offset, _value.asBytes()); }
void writeAddress(uint64_t _offset, util::h160 _value) { accessMemory(_offset, 20); writeMemory(_offset, _value.asBytes()); }
/// Helper for eth.* builtins. Reads from memory (little-endian) and returns the value.
u256 readU256(uint64_t _offset, size_t _croppedTo = 32);
u256 readU128(uint64_t _offset) { return readU256(_offset, 16); }
u256 readAddress(uint64_t _offset) { return readU256(_offset, 20); }
/// Helper for eth.* builtins. Reads from memory (as a byte string).
util::h256 readBytes32(uint64_t _offset) { accessMemory(_offset, 32); return util::h256(readMemory(_offset, 32)); }
util::h160 readAddress(uint64_t _offset) { accessMemory(_offset, 20); return util::h160(readMemory(_offset, 20)); }
void logTrace(evmasm::Instruction _instruction, std::vector<u256> const& _arguments = {}, bytes const& _data = {});
/// Appends a log to the trace representing an instruction or similar operation by string,