|
|
@ -40,159 +40,157 @@ namespace test |
|
|
|
class ExecutionFramework |
|
|
|
{ |
|
|
|
public: |
|
|
|
ExecutionFramework() { g_logVerbosity = 0; } |
|
|
|
|
|
|
|
bytes const& execute(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") |
|
|
|
{ |
|
|
|
m_compiler.reset(false, m_addStandardSources); |
|
|
|
m_compiler.addSource("", _sourceCode); |
|
|
|
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize), "Compiling contract failed"); |
|
|
|
|
|
|
|
bytes code = m_compiler.getBytecode(_contractName); |
|
|
|
sendMessage(code, true, _value); |
|
|
|
return m_output; |
|
|
|
} |
|
|
|
|
|
|
|
bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") |
|
|
|
{ |
|
|
|
execute(_sourceCode, _value, _contractName); |
|
|
|
BOOST_REQUIRE(!m_output.empty()); |
|
|
|
return m_output; |
|
|
|
} |
|
|
|
|
|
|
|
template <class... Args> |
|
|
|
bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, Args const&... _arguments) |
|
|
|
{ |
|
|
|
FixedHash<4> hash(dev::sha3(_sig)); |
|
|
|
sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value); |
|
|
|
return m_output; |
|
|
|
} |
|
|
|
|
|
|
|
template <class... Args> |
|
|
|
bytes const& callContractFunction(std::string _sig, Args const&... _arguments) |
|
|
|
{ |
|
|
|
return callContractFunctionWithValue(_sig, 0, _arguments...); |
|
|
|
} |
|
|
|
|
|
|
|
template <class CppFunction, class... Args> |
|
|
|
void testSolidityAgainstCpp(std::string _sig, CppFunction const& _cppFunction, Args const&... _arguments) |
|
|
|
{ |
|
|
|
bytes solidityResult = callContractFunction(_sig, _arguments...); |
|
|
|
bytes cppResult = callCppAndEncodeResult(_cppFunction, _arguments...); |
|
|
|
BOOST_CHECK_MESSAGE( |
|
|
|
solidityResult == cppResult, "Computed values do not match.\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult)); |
|
|
|
} |
|
|
|
|
|
|
|
template <class CppFunction, class... Args> |
|
|
|
void testSolidityAgainstCppOnRange( |
|
|
|
std::string _sig, |
|
|
|
CppFunction const& _cppFunction, |
|
|
|
u256 const& _rangeStart, |
|
|
|
u256 const& _rangeEnd |
|
|
|
) |
|
|
|
{ |
|
|
|
for (u256 argument = _rangeStart; argument < _rangeEnd; ++argument) |
|
|
|
ExecutionFramework() { g_logVerbosity = 0; } |
|
|
|
|
|
|
|
bytes const& execute(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") |
|
|
|
{ |
|
|
|
bytes solidityResult = callContractFunction(_sig, argument); |
|
|
|
bytes cppResult = callCppAndEncodeResult(_cppFunction, argument); |
|
|
|
BOOST_CHECK_MESSAGE( |
|
|
|
solidityResult == cppResult, |
|
|
|
"Computed values do not match.\nSolidity: " + |
|
|
|
toHex(solidityResult) + |
|
|
|
"\nC++: " + |
|
|
|
toHex(cppResult) + |
|
|
|
"\nArgument: " + |
|
|
|
toHex(encode(argument)) |
|
|
|
); |
|
|
|
m_compiler.reset(false, m_addStandardSources); |
|
|
|
m_compiler.addSource("", _sourceCode); |
|
|
|
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize), "Compiling contract failed"); |
|
|
|
bytes code = m_compiler.getBytecode(_contractName); |
|
|
|
sendMessage(code, true, _value); |
|
|
|
return m_output; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static bytes encode(bool _value) { return encode(byte(_value)); } |
|
|
|
static bytes encode(int _value) { return encode(u256(_value)); } |
|
|
|
static bytes encode(char const* _value) { return encode(std::string(_value)); } |
|
|
|
static bytes encode(byte _value) { return bytes(31, 0) + bytes{_value}; } |
|
|
|
static bytes encode(u256 const& _value) { return toBigEndian(_value); } |
|
|
|
static bytes encode(h256 const& _value) { return _value.asBytes(); } |
|
|
|
static bytes encode(bytes const& _value, bool _padLeft = true) |
|
|
|
{ |
|
|
|
bytes padding = bytes((32 - _value.size() % 32) % 32, 0); |
|
|
|
return _padLeft ? padding + _value : _value + padding; |
|
|
|
} |
|
|
|
static bytes encode(std::string const& _value) { return encode(asBytes(_value), false); } |
|
|
|
|
|
|
|
template <class FirstArg, class... Args> |
|
|
|
static bytes encodeArgs(FirstArg const& _firstArg, Args const&... _followingArgs) |
|
|
|
{ |
|
|
|
return encode(_firstArg) + encodeArgs(_followingArgs...); |
|
|
|
} |
|
|
|
static bytes encodeArgs() |
|
|
|
{ |
|
|
|
return bytes(); |
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
template <class CppFunction, class... Args> |
|
|
|
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments) |
|
|
|
-> typename std::enable_if<std::is_void<decltype(_cppFunction(_arguments...))>::value, bytes>::type |
|
|
|
{ |
|
|
|
_cppFunction(_arguments...); |
|
|
|
return bytes(); |
|
|
|
} |
|
|
|
template <class CppFunction, class... Args> |
|
|
|
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments) |
|
|
|
-> typename std::enable_if<!std::is_void<decltype(_cppFunction(_arguments...))>::value, bytes>::type |
|
|
|
{ |
|
|
|
return encode(_cppFunction(_arguments...)); |
|
|
|
} |
|
|
|
bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") |
|
|
|
{ |
|
|
|
execute(_sourceCode, _value, _contractName); |
|
|
|
BOOST_REQUIRE(!m_output.empty()); |
|
|
|
return m_output; |
|
|
|
} |
|
|
|
|
|
|
|
protected: |
|
|
|
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0) |
|
|
|
{ |
|
|
|
m_state.addBalance(m_sender, _value); // just in case
|
|
|
|
eth::Executive executive(m_state, eth::LastHashes(), 0); |
|
|
|
eth::Transaction t = _isCreation ? eth::Transaction( |
|
|
|
_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec() |
|
|
|
) : eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); |
|
|
|
|
|
|
|
bytes transactionRLP = t.rlp(); |
|
|
|
try |
|
|
|
template <class... Args> |
|
|
|
bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, Args const&... _arguments) |
|
|
|
{ |
|
|
|
// this will throw since the transaction is invalid, but it should nevertheless store the transaction
|
|
|
|
executive.initialize(&transactionRLP); |
|
|
|
executive.execute(); |
|
|
|
FixedHash<4> hash(dev::sha3(_sig)); |
|
|
|
sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value); |
|
|
|
return m_output; |
|
|
|
} |
|
|
|
catch (...) {} |
|
|
|
if (_isCreation) |
|
|
|
|
|
|
|
template <class... Args> |
|
|
|
bytes const& callContractFunction(std::string _sig, Args const&... _arguments) |
|
|
|
{ |
|
|
|
BOOST_REQUIRE(!executive.create(m_sender, _value, m_gasPrice, m_gas, &_data, m_sender)); |
|
|
|
m_contractAddress = executive.newAddress(); |
|
|
|
BOOST_REQUIRE(m_contractAddress); |
|
|
|
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); |
|
|
|
return callContractFunctionWithValue(_sig, 0, _arguments...); |
|
|
|
} |
|
|
|
else |
|
|
|
|
|
|
|
template <class CppFunction, class... Args> |
|
|
|
void testSolidityAgainstCpp(std::string _sig, CppFunction const& _cppFunction, Args const&... _arguments) |
|
|
|
{ |
|
|
|
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); |
|
|
|
BOOST_REQUIRE(!executive.call(m_contractAddress, m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas, m_sender)); |
|
|
|
bytes solidityResult = callContractFunction(_sig, _arguments...); |
|
|
|
bytes cppResult = callCppAndEncodeResult(_cppFunction, _arguments...); |
|
|
|
BOOST_CHECK_MESSAGE( |
|
|
|
solidityResult == cppResult, |
|
|
|
"Computed values do not match.\nSolidity: " + |
|
|
|
toHex(solidityResult) + |
|
|
|
"\nC++: " + |
|
|
|
toHex(cppResult)); |
|
|
|
} |
|
|
|
BOOST_REQUIRE(executive.go()); |
|
|
|
m_state.noteSending(m_sender); |
|
|
|
executive.finalize(); |
|
|
|
m_gasUsed = executive.gasUsed(); |
|
|
|
m_output = executive.out().toVector(); |
|
|
|
m_logs = executive.logs(); |
|
|
|
} |
|
|
|
|
|
|
|
bool m_optimize = false; |
|
|
|
bool m_addStandardSources = false; |
|
|
|
dev::solidity::CompilerStack m_compiler; |
|
|
|
Address m_sender; |
|
|
|
Address m_contractAddress; |
|
|
|
eth::State m_state; |
|
|
|
u256 const m_gasPrice = 100 * eth::szabo; |
|
|
|
u256 const m_gas = 100000000; |
|
|
|
bytes m_output; |
|
|
|
eth::LogEntries m_logs; |
|
|
|
u256 m_gasUsed; |
|
|
|
|
|
|
|
template <class CppFunction, class... Args> |
|
|
|
void testSolidityAgainstCppOnRange(std::string _sig, CppFunction const& _cppFunction, u256 const& _rangeStart, u256 const& _rangeEnd) |
|
|
|
{ |
|
|
|
for (u256 argument = _rangeStart; argument < _rangeEnd; ++argument) |
|
|
|
{ |
|
|
|
bytes solidityResult = callContractFunction(_sig, argument); |
|
|
|
bytes cppResult = callCppAndEncodeResult(_cppFunction, argument); |
|
|
|
BOOST_CHECK_MESSAGE( |
|
|
|
solidityResult == cppResult, |
|
|
|
"Computed values do not match.\nSolidity: " + |
|
|
|
toHex(solidityResult) + |
|
|
|
"\nC++: " + |
|
|
|
toHex(cppResult) + |
|
|
|
"\nArgument: " + |
|
|
|
toHex(encode(argument)) |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static bytes encode(bool _value) { return encode(byte(_value)); } |
|
|
|
static bytes encode(int _value) { return encode(u256(_value)); } |
|
|
|
static bytes encode(char const* _value) { return encode(std::string(_value)); } |
|
|
|
static bytes encode(byte _value) { return bytes(31, 0) + bytes{_value}; } |
|
|
|
static bytes encode(u256 const& _value) { return toBigEndian(_value); } |
|
|
|
static bytes encode(h256 const& _value) { return _value.asBytes(); } |
|
|
|
static bytes encode(bytes const& _value, bool _padLeft = true) |
|
|
|
{ |
|
|
|
bytes padding = bytes((32 - _value.size() % 32) % 32, 0); |
|
|
|
return _padLeft ? padding + _value : _value + padding; |
|
|
|
} |
|
|
|
static bytes encode(std::string const& _value) { return encode(asBytes(_value), false); } |
|
|
|
|
|
|
|
template <class FirstArg, class... Args> |
|
|
|
static bytes encodeArgs(FirstArg const& _firstArg, Args const&... _followingArgs) |
|
|
|
{ |
|
|
|
return encode(_firstArg) + encodeArgs(_followingArgs...); |
|
|
|
} |
|
|
|
static bytes encodeArgs() |
|
|
|
{ |
|
|
|
return bytes(); |
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
template <class CppFunction, class... Args> |
|
|
|
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments) |
|
|
|
-> typename std::enable_if<std::is_void<decltype(_cppFunction(_arguments...))>::value, bytes>::type |
|
|
|
{ |
|
|
|
_cppFunction(_arguments...); |
|
|
|
return bytes(); |
|
|
|
} |
|
|
|
template <class CppFunction, class... Args> |
|
|
|
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments) |
|
|
|
-> typename std::enable_if<!std::is_void<decltype(_cppFunction(_arguments...))>::value, bytes>::type |
|
|
|
{ |
|
|
|
return encode(_cppFunction(_arguments...)); |
|
|
|
} |
|
|
|
|
|
|
|
protected: |
|
|
|
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0) |
|
|
|
{ |
|
|
|
m_state.addBalance(m_sender, _value); // just in case
|
|
|
|
eth::Executive executive(m_state, eth::LastHashes(), 0); |
|
|
|
eth::Transaction t = |
|
|
|
_isCreation ? |
|
|
|
eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) : |
|
|
|
eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); |
|
|
|
bytes transactionRLP = t.rlp(); |
|
|
|
try |
|
|
|
{ |
|
|
|
// this will throw since the transaction is invalid, but it should nevertheless store the transaction
|
|
|
|
executive.initialize(&transactionRLP); |
|
|
|
executive.execute(); |
|
|
|
} |
|
|
|
catch (...) {} |
|
|
|
if (_isCreation) |
|
|
|
{ |
|
|
|
BOOST_REQUIRE(!executive.create(m_sender, _value, m_gasPrice, m_gas, &_data, m_sender)); |
|
|
|
m_contractAddress = executive.newAddress(); |
|
|
|
BOOST_REQUIRE(m_contractAddress); |
|
|
|
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); |
|
|
|
BOOST_REQUIRE(!executive.call(m_contractAddress, m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas, m_sender)); |
|
|
|
} |
|
|
|
BOOST_REQUIRE(executive.go()); |
|
|
|
m_state.noteSending(m_sender); |
|
|
|
executive.finalize(); |
|
|
|
m_gasUsed = executive.gasUsed(); |
|
|
|
m_output = executive.out().toVector(); |
|
|
|
m_logs = executive.logs(); |
|
|
|
} |
|
|
|
|
|
|
|
bool m_optimize = false; |
|
|
|
bool m_addStandardSources = false; |
|
|
|
dev::solidity::CompilerStack m_compiler; |
|
|
|
Address m_sender; |
|
|
|
Address m_contractAddress; |
|
|
|
eth::State m_state; |
|
|
|
u256 const m_gasPrice = 100 * eth::szabo; |
|
|
|
u256 const m_gas = 100000000; |
|
|
|
bytes m_output; |
|
|
|
eth::LogEntries m_logs; |
|
|
|
u256 m_gasUsed; |
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|