2014-02-17 21:07:09 +00:00
/*
This file is part of cpp - ethereum .
cpp - ethereum is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
cpp - ethereum is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with cpp - ethereum . If not , see < http : //www.gnu.org/licenses/>.
*/
2014-09-27 10:51:34 +00:00
/** @file vm.cpp
2014-02-17 21:07:09 +00:00
* @ author Gav Wood < i @ gavwood . com >
* @ date 2014
2014-09-27 10:51:34 +00:00
* vm test functions .
2014-02-17 21:07:09 +00:00
*/
2014-09-27 10:51:34 +00:00
# include "vm.h"
2014-10-07 21:07:56 +00:00
# include <libdevcore/CommonIO.h>
2014-10-17 08:58:10 +00:00
# include <libevmjit/VM.h>
2014-10-14 10:41:01 +00:00
# include <boost/filesystem/path.hpp>
2014-09-25 09:18:32 +00:00
2014-10-17 14:07:53 +00:00
//#define FILL_TESTS
2014-04-19 17:55:20 +00:00
2014-02-17 21:07:09 +00:00
using namespace std ;
2014-02-21 19:18:30 +00:00
using namespace json_spirit ;
2014-09-05 15:09:58 +00:00
using namespace dev ;
using namespace dev : : eth ;
2014-09-27 10:51:34 +00:00
using namespace dev : : test ;
2014-02-17 21:07:09 +00:00
2014-10-08 15:51:51 +00:00
FakeExtVM : : FakeExtVM ( eth : : BlockInfo const & _previousBlock , eth : : BlockInfo const & _currentBlock , unsigned _depth ) : /// TODO: XXX: remove the default argument & fix.
ExtVMFace ( Address ( ) , Address ( ) , Address ( ) , 0 , 1 , bytesConstRef ( ) , bytesConstRef ( ) , _previousBlock , _currentBlock , _depth ) { }
2014-10-01 11:59:07 +00:00
2014-10-20 15:36:26 +00:00
h160 FakeExtVM : : create ( u256 _endowment , u256 * _gas , bytesConstRef _init , OnOpFunc const & )
2014-02-17 21:07:09 +00:00
{
2014-10-06 08:17:27 +00:00
Transaction t ;
t . value = _endowment ;
t . gasPrice = gasPrice ;
t . gas = * _gas ;
t . data = _init . toBytes ( ) ;
2014-09-27 10:51:34 +00:00
m_s . noteSending ( myAddress ) ;
m_ms . internal . resize ( m_ms . internal . size ( ) + 1 ) ;
2014-10-20 15:36:26 +00:00
auto ret = m_s . create ( myAddress , _endowment , gasPrice , _gas , _init , origin , & suicides , & m_ms ? & ( m_ms . internal . back ( ) ) : nullptr , { } , 1 ) ;
2014-10-01 11:59:07 +00:00
if ( ! m_ms . internal . back ( ) . from )
2014-09-27 10:51:34 +00:00
m_ms . internal . pop_back ( ) ;
if ( get < 0 > ( addresses [ myAddress ] ) > = _endowment )
2014-03-24 23:51:40 +00:00
{
2014-09-27 10:51:34 +00:00
get < 1 > ( addresses [ myAddress ] ) + + ;
get < 0 > ( addresses [ ret ] ) = _endowment ;
get < 3 > ( addresses [ ret ] ) = m_s . code ( ret ) ;
2014-03-24 23:51:40 +00:00
}
2014-09-25 09:18:32 +00:00
2014-10-06 19:42:41 +00:00
t . receiveAddress = ret ;
callcreates . push_back ( t ) ;
2014-09-27 10:51:34 +00:00
return ret ;
}
2014-03-24 23:51:40 +00:00
2014-10-20 15:36:26 +00:00
bool FakeExtVM : : call ( Address _receiveAddress , u256 _value , bytesConstRef _data , u256 * _gas , bytesRef _out , OnOpFunc const & , Address _myAddressOverride = Address ( ) , Address _codeAddressOverride = Address ( ) )
2014-09-27 10:51:34 +00:00
{
2014-10-06 19:42:41 +00:00
u256 contractgas = 0xffff ;
2014-10-06 08:17:27 +00:00
Transaction t ;
t . value = _value ;
t . gasPrice = gasPrice ;
t . gas = * _gas ;
t . data = _data . toVector ( ) ;
t . receiveAddress = _receiveAddress ;
2014-10-06 19:42:41 +00:00
callcreates . push_back ( t ) ;
string codeOf_CodeAddress = _codeAddressOverride ? toHex ( get < 3 > ( addresses [ _codeAddressOverride ] ) ) : toHex ( get < 3 > ( addresses [ _receiveAddress ] ) ) ;
string sizeOfCode = toHex ( toCompactBigEndian ( ( codeOf_CodeAddress . size ( ) + 1 ) / 2 ) ) ;
string codeOf_SenderAddress = toHex ( get < 3 > ( addresses [ myAddress ] ) ) ;
string sizeOfSenderCode = toHex ( toCompactBigEndian ( ( codeOf_SenderAddress . size ( ) + 1 ) / 2 ) ) ;
if ( codeOf_SenderAddress . size ( ) )
{
// create init code that returns given contract code
string initStringHex = " { (CODECOPY 0 (- (CODESIZE) 0x " + sizeOfSenderCode + " ) 0x " + sizeOfSenderCode + " ) (RETURN 0 0x " + sizeOfSenderCode + " )} " ;
bytes initBytes = compileLLL ( initStringHex , true , NULL ) ;
initBytes + = fromHex ( codeOf_SenderAddress ) ;
bytesConstRef init ( & initBytes ) ;
2014-10-06 08:17:27 +00:00
2014-10-06 19:42:41 +00:00
if ( ! m_s . addresses ( ) . count ( myAddress ) )
{
m_ms . internal . resize ( m_ms . internal . size ( ) + 1 ) ;
2014-10-20 15:36:26 +00:00
auto na = m_s . createNewAddress ( myAddress , myAddress , balance ( myAddress ) , gasPrice , & contractgas , init , origin , & suicides , & m_ms ? & ( m_ms . internal . back ( ) ) : nullptr , { } , 1 ) ;
2014-10-06 19:42:41 +00:00
if ( ! m_ms . internal . back ( ) . from )
m_ms . internal . pop_back ( ) ;
if ( na ! = myAddress )
{
cnote < < " not able to call to : " < < myAddress < < " \n " ;
cnote < < " in FakeExtVM you can only make a call to " < < na < < " \n " ;
2014-10-14 10:41:01 +00:00
BOOST_THROW_EXCEPTION ( FakeExtVMFailure ( ) < < errinfo_comment ( " Address not callable in FakeExtVM \n " ) < < errinfo_wrongAddress ( toString ( myAddress ) ) ) ;
2014-10-06 19:42:41 +00:00
return false ;
}
}
}
2014-02-21 19:18:30 +00:00
2014-10-06 19:42:41 +00:00
if ( codeOf_CodeAddress . size ( ) )
2014-02-21 19:18:30 +00:00
{
2014-10-01 11:59:07 +00:00
// create init code that returns given contract code
string initStringHex = " { (CODECOPY 0 (- (CODESIZE) 0x " + sizeOfCode + " ) 0x " + sizeOfCode + " ) (RETURN 0 0x " + sizeOfCode + " )} " ;
bytes initBytes = compileLLL ( initStringHex , true , NULL ) ;
2014-10-06 19:42:41 +00:00
initBytes + = fromHex ( codeOf_CodeAddress ) ;
2014-09-27 10:51:34 +00:00
bytesConstRef init ( & initBytes ) ;
2014-02-21 19:18:30 +00:00
2014-10-06 19:42:41 +00:00
if ( ! m_s . addresses ( ) . count ( _codeAddressOverride ? _codeAddressOverride : _receiveAddress ) )
2014-02-21 19:18:30 +00:00
{
2014-09-27 10:51:34 +00:00
m_s . noteSending ( myAddress ) ;
m_ms . internal . resize ( m_ms . internal . size ( ) + 1 ) ;
2014-10-06 19:42:41 +00:00
auto na = m_s . createNewAddress ( _codeAddressOverride ? _codeAddressOverride : _receiveAddress , myAddress , balance ( _codeAddressOverride ? _codeAddressOverride : _receiveAddress ) , gasPrice , & contractgas , init , origin , & suicides , & m_ms ? & ( m_ms . internal . back ( ) ) : nullptr , OnOpFunc ( ) , 1 ) ;
2014-10-01 11:59:07 +00:00
if ( ! m_ms . internal . back ( ) . from )
2014-09-27 10:51:34 +00:00
m_ms . internal . pop_back ( ) ;
2014-10-06 19:42:41 +00:00
if ( na ! = ( _codeAddressOverride ? _codeAddressOverride : _receiveAddress ) )
2014-09-27 10:51:34 +00:00
{
2014-10-06 19:42:41 +00:00
cnote < < " not able to call to : " < < ( _codeAddressOverride ? _codeAddressOverride : _receiveAddress ) < < " \n " ;
2014-09-27 10:51:34 +00:00
cnote < < " in FakeExtVM you can only make a call to " < < na < < " \n " ;
2014-10-14 10:41:01 +00:00
BOOST_THROW_EXCEPTION ( FakeExtVMFailure ( ) < < errinfo_comment ( " Address not callable in FakeExtVM \n " ) < < errinfo_wrongAddress ( toString ( _codeAddressOverride ? _codeAddressOverride : _receiveAddress ) ) ) ;
2014-09-27 10:51:34 +00:00
return false ;
}
2014-02-21 19:18:30 +00:00
}
2014-09-27 10:51:34 +00:00
m_ms . internal . resize ( m_ms . internal . size ( ) + 1 ) ;
2014-10-06 19:42:41 +00:00
auto ret = m_s . call ( _receiveAddress , _codeAddressOverride ? _codeAddressOverride : _receiveAddress , _myAddressOverride ? _myAddressOverride : myAddress , _value , gasPrice , _data , _gas , _out , origin , & suicides , & ( m_ms . internal . back ( ) ) , OnOpFunc ( ) , 1 ) ;
2014-09-27 10:51:34 +00:00
if ( ! m_ms . internal . back ( ) . from )
m_ms . internal . pop_back ( ) ;
2014-10-06 19:42:41 +00:00
// get correct balances, (also for sucicides in the call function)
for ( auto const & f : addresses )
{
if ( m_s . addressInUse ( f . first ) )
get < 0 > ( addresses [ f . first ] ) = m_s . balance ( f . first ) ;
}
2014-09-27 10:51:34 +00:00
if ( ! ret )
return false ;
2014-10-06 19:42:41 +00:00
// do suicides
for ( auto const & f : suicides )
addresses . erase ( f ) ;
2014-09-27 10:51:34 +00:00
2014-10-06 19:42:41 +00:00
// get storage
if ( ( get < 0 > ( addresses [ myAddress ] ) > = _value ) & & ( suicides . find ( _receiveAddress ) = = suicides . end ( ) ) )
{
2014-09-27 10:51:34 +00:00
for ( auto const & j : m_s . storage ( _receiveAddress ) )
{
u256 adr ( j . first ) ;
if ( ( j . second ! = 0 ) )
get < 2 > ( addresses [ _receiveAddress ] ) [ adr ] = j . second ;
}
2014-03-24 23:51:40 +00:00
}
2014-02-21 19:18:30 +00:00
}
2014-09-27 10:51:34 +00:00
else
addresses . erase ( _receiveAddress ) ; // for the sake of comparison
return true ;
}
2014-02-21 19:18:30 +00:00
2014-09-27 10:51:34 +00:00
void FakeExtVM : : setTransaction ( Address _caller , u256 _value , u256 _gasPrice , bytes const & _data )
{
caller = origin = _caller ;
value = _value ;
data = & ( thisTxData = _data ) ;
gasPrice = _gasPrice ;
}
2014-10-01 11:59:07 +00:00
2014-09-27 10:51:34 +00:00
void FakeExtVM : : setContract ( Address _myAddress , u256 _myBalance , u256 _myNonce , map < u256 , u256 > const & _storage , bytes const & _code )
{
myAddress = _myAddress ;
set ( myAddress , _myBalance , _myNonce , _storage , _code ) ;
}
2014-10-01 11:59:07 +00:00
2014-09-27 10:51:34 +00:00
void FakeExtVM : : set ( Address _a , u256 _myBalance , u256 _myNonce , map < u256 , u256 > const & _storage , bytes const & _code )
{
get < 0 > ( addresses [ _a ] ) = _myBalance ;
get < 1 > ( addresses [ _a ] ) = _myNonce ;
get < 2 > ( addresses [ _a ] ) = _storage ;
get < 3 > ( addresses [ _a ] ) = _code ;
}
2014-02-21 19:18:30 +00:00
2014-09-27 10:51:34 +00:00
void FakeExtVM : : reset ( u256 _myBalance , u256 _myNonce , map < u256 , u256 > const & _storage )
{
callcreates . clear ( ) ;
addresses . clear ( ) ;
set ( myAddress , _myBalance , _myNonce , _storage , get < 3 > ( addresses [ myAddress ] ) ) ;
}
u256 FakeExtVM : : toInt ( mValue const & _v )
{
switch ( _v . type ( ) )
2014-06-27 19:39:46 +00:00
{
2014-09-27 10:51:34 +00:00
case str_type : return u256 ( _v . get_str ( ) ) ;
case int_type : return ( u256 ) _v . get_uint64 ( ) ;
case bool_type : return ( u256 ) ( uint64_t ) _v . get_bool ( ) ;
case real_type : return ( u256 ) ( uint64_t ) _v . get_real ( ) ;
default : cwarn < < " Bad type for scalar: " < < _v . type ( ) ;
2014-06-27 19:39:46 +00:00
}
2014-09-27 10:51:34 +00:00
return 0 ;
}
2014-06-27 19:39:46 +00:00
2014-09-27 10:51:34 +00:00
byte FakeExtVM : : toByte ( mValue const & _v )
{
switch ( _v . type ( ) )
2014-06-27 19:39:46 +00:00
{
2014-09-27 10:51:34 +00:00
case str_type : return ( byte ) stoi ( _v . get_str ( ) ) ;
case int_type : return ( byte ) _v . get_uint64 ( ) ;
case bool_type : return ( byte ) _v . get_bool ( ) ;
case real_type : return ( byte ) _v . get_real ( ) ;
default : cwarn < < " Bad type for scalar: " < < _v . type ( ) ;
2014-06-27 19:39:46 +00:00
}
2014-09-27 10:51:34 +00:00
return 0 ;
}
void FakeExtVM : : push ( mObject & o , string const & _n , u256 _v )
{
// if (_v < (u256)1 << 64)
// o[_n] = (uint64_t)_v;
// else
o [ _n ] = toString ( _v ) ;
}
2014-06-27 19:39:46 +00:00
2014-09-27 10:51:34 +00:00
void FakeExtVM : : push ( mArray & a , u256 _v )
{
// if (_v < (u256)1 << 64)
// a.push_back((uint64_t)_v);
// else
a . push_back ( toString ( _v ) ) ;
}
mObject FakeExtVM : : exportEnv ( )
{
mObject ret ;
ret [ " previousHash " ] = toString ( previousBlock . hash ) ;
push ( ret , " currentDifficulty " , currentBlock . difficulty ) ;
push ( ret , " currentTimestamp " , currentBlock . timestamp ) ;
ret [ " currentCoinbase " ] = toString ( currentBlock . coinbaseAddress ) ;
push ( ret , " currentNumber " , currentBlock . number ) ;
push ( ret , " currentGasLimit " , currentBlock . gasLimit ) ;
return ret ;
}
void FakeExtVM : : importEnv ( mObject & _o )
{
2014-10-14 13:13:38 +00:00
// cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest)
assert ( _o . count ( " previousHash " ) > 0 ) ;
assert ( _o . count ( " currentGasLimit " ) > 0 ) ;
assert ( _o . count ( " currentDifficulty " ) > 0 ) ;
assert ( _o . count ( " currentTimestamp " ) > 0 ) ;
assert ( _o . count ( " currentCoinbase " ) > 0 ) ;
assert ( _o . count ( " currentNumber " ) > 0 ) ;
2014-09-27 10:51:34 +00:00
previousBlock . hash = h256 ( _o [ " previousHash " ] . get_str ( ) ) ;
currentBlock . number = toInt ( _o [ " currentNumber " ] ) ;
currentBlock . gasLimit = toInt ( _o [ " currentGasLimit " ] ) ;
currentBlock . difficulty = toInt ( _o [ " currentDifficulty " ] ) ;
currentBlock . timestamp = toInt ( _o [ " currentTimestamp " ] ) ;
currentBlock . coinbaseAddress = Address ( _o [ " currentCoinbase " ] . get_str ( ) ) ;
}
mObject FakeExtVM : : exportState ( )
{
mObject ret ;
for ( auto const & a : addresses )
2014-02-21 19:18:30 +00:00
{
2014-09-27 10:51:34 +00:00
mObject o ;
push ( o , " balance " , get < 0 > ( a . second ) ) ;
push ( o , " nonce " , get < 1 > ( a . second ) ) ;
2014-02-21 19:18:30 +00:00
2014-09-27 10:51:34 +00:00
{
mObject store ;
for ( auto const & s : get < 2 > ( a . second ) )
2014-10-06 19:42:41 +00:00
store [ " 0x " + toHex ( toCompactBigEndian ( s . first ) ) ] = " 0x " + toHex ( toCompactBigEndian ( s . second ) ) ;
2014-09-27 10:51:34 +00:00
o [ " storage " ] = store ;
2014-02-21 19:18:30 +00:00
}
2014-09-27 10:51:34 +00:00
o [ " code " ] = " 0x " + toHex ( get < 3 > ( a . second ) ) ;
ret [ toString ( a . first ) ] = o ;
2014-02-21 19:18:30 +00:00
}
2014-09-27 10:51:34 +00:00
return ret ;
}
2014-02-21 19:18:30 +00:00
2014-09-27 10:51:34 +00:00
void FakeExtVM : : importState ( mObject & _object )
{
for ( auto const & i : _object )
2014-02-21 19:18:30 +00:00
{
2014-09-27 10:51:34 +00:00
mObject o = i . second . get_obj ( ) ;
2014-10-14 13:13:38 +00:00
// cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest)
assert ( o . count ( " balance " ) > 0 ) ;
assert ( o . count ( " nonce " ) > 0 ) ;
assert ( o . count ( " storage " ) > 0 ) ;
assert ( o . count ( " code " ) > 0 ) ;
2014-09-27 10:51:34 +00:00
auto & a = addresses [ Address ( i . first ) ] ;
get < 0 > ( a ) = toInt ( o [ " balance " ] ) ;
get < 1 > ( a ) = toInt ( o [ " nonce " ] ) ;
for ( auto const & j : o [ " storage " ] . get_obj ( ) )
2014-10-06 19:42:41 +00:00
get < 2 > ( a ) [ toInt ( j . first ) ] = toInt ( j . second ) ;
2014-07-06 14:16:53 +00:00
2014-09-27 10:51:34 +00:00
if ( o [ " code " ] . type ( ) = = str_type )
if ( o [ " code " ] . get_str ( ) . find_first_of ( " 0x " ) ! = 0 )
get < 3 > ( a ) = compileLLL ( o [ " code " ] . get_str ( ) , false ) ;
2014-05-12 13:40:26 +00:00
else
2014-09-27 10:51:34 +00:00
get < 3 > ( a ) = fromHex ( o [ " code " ] . get_str ( ) . substr ( 2 ) ) ;
else
{
get < 3 > ( a ) . clear ( ) ;
for ( auto const & j : o [ " code " ] . get_array ( ) )
get < 3 > ( a ) . push_back ( toByte ( j ) ) ;
2014-02-21 19:18:30 +00:00
}
}
2014-09-27 10:51:34 +00:00
}
2014-02-21 19:18:30 +00:00
2014-09-27 10:51:34 +00:00
mObject FakeExtVM : : exportExec ( )
{
mObject ret ;
ret [ " address " ] = toString ( myAddress ) ;
ret [ " caller " ] = toString ( caller ) ;
ret [ " origin " ] = toString ( origin ) ;
push ( ret , " value " , value ) ;
push ( ret , " gasPrice " , gasPrice ) ;
push ( ret , " gas " , gas ) ;
ret [ " data " ] = " 0x " + toHex ( data ) ;
ret [ " code " ] = " 0x " + toHex ( code ) ;
return ret ;
}
2014-02-21 19:18:30 +00:00
2014-09-27 10:51:34 +00:00
void FakeExtVM : : importExec ( mObject & _o )
{
2014-10-14 13:13:38 +00:00
// cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest)
assert ( _o . count ( " address " ) > 0 ) ;
assert ( _o . count ( " caller " ) > 0 ) ;
assert ( _o . count ( " origin " ) > 0 ) ;
assert ( _o . count ( " value " ) > 0 ) ;
assert ( _o . count ( " data " ) > 0 ) ;
assert ( _o . count ( " gasPrice " ) > 0 ) ;
assert ( _o . count ( " gas " ) > 0 ) ;
2014-09-27 10:51:34 +00:00
myAddress = Address ( _o [ " address " ] . get_str ( ) ) ;
caller = Address ( _o [ " caller " ] . get_str ( ) ) ;
origin = Address ( _o [ " origin " ] . get_str ( ) ) ;
value = toInt ( _o [ " value " ] ) ;
gasPrice = toInt ( _o [ " gasPrice " ] ) ;
gas = toInt ( _o [ " gas " ] ) ;
thisTxCode . clear ( ) ;
code = & thisTxCode ;
if ( _o [ " code " ] . type ( ) = = str_type )
if ( _o [ " code " ] . get_str ( ) . find_first_of ( " 0x " ) = = 0 )
thisTxCode = compileLLL ( _o [ " code " ] . get_str ( ) ) ;
2014-06-27 19:39:46 +00:00
else
2014-09-27 10:51:34 +00:00
thisTxCode = fromHex ( _o [ " code " ] . get_str ( ) . substr ( 2 ) ) ;
else if ( _o [ " code " ] . type ( ) = = array_type )
for ( auto const & j : _o [ " code " ] . get_array ( ) )
thisTxCode . push_back ( toByte ( j ) ) ;
else
code . reset ( ) ;
thisTxData . clear ( ) ;
if ( _o [ " data " ] . type ( ) = = str_type )
if ( _o [ " data " ] . get_str ( ) . find_first_of ( " 0x " ) = = 0 )
thisTxData = fromHex ( _o [ " data " ] . get_str ( ) . substr ( 2 ) ) ;
2014-05-12 13:40:26 +00:00
else
2014-09-27 10:51:34 +00:00
thisTxData = fromHex ( _o [ " data " ] . get_str ( ) ) ;
else
for ( auto const & j : _o [ " data " ] . get_array ( ) )
thisTxData . push_back ( toByte ( j ) ) ;
data = & thisTxData ;
}
2014-02-21 19:18:30 +00:00
2014-09-27 10:51:34 +00:00
mArray FakeExtVM : : exportCallCreates ( )
{
mArray ret ;
for ( Transaction const & tx : callcreates )
2014-02-21 19:18:30 +00:00
{
2014-09-27 10:51:34 +00:00
mObject o ;
o [ " destination " ] = toString ( tx . receiveAddress ) ;
push ( o , " gasLimit " , tx . gas ) ;
push ( o , " value " , tx . value ) ;
o [ " data " ] = " 0x " + toHex ( tx . data ) ;
ret . push_back ( o ) ;
2014-02-21 19:18:30 +00:00
}
2014-09-27 10:51:34 +00:00
return ret ;
}
2014-02-21 19:18:30 +00:00
2014-09-27 10:51:34 +00:00
void FakeExtVM : : importCallCreates ( mArray & _callcreates )
{
for ( mValue & v : _callcreates )
2014-02-21 19:18:30 +00:00
{
2014-09-27 10:51:34 +00:00
auto tx = v . get_obj ( ) ;
BOOST_REQUIRE ( tx . count ( " data " ) > 0 ) ;
BOOST_REQUIRE ( tx . count ( " value " ) > 0 ) ;
BOOST_REQUIRE ( tx . count ( " destination " ) > 0 ) ;
BOOST_REQUIRE ( tx . count ( " gasLimit " ) > 0 ) ;
Transaction t ;
t . receiveAddress = Address ( tx [ " destination " ] . get_str ( ) ) ;
t . value = toInt ( tx [ " value " ] ) ;
t . gas = toInt ( tx [ " gasLimit " ] ) ;
if ( tx [ " data " ] . type ( ) = = str_type )
if ( tx [ " data " ] . get_str ( ) . find_first_of ( " 0x " ) = = 0 )
t . data = fromHex ( tx [ " data " ] . get_str ( ) . substr ( 2 ) ) ;
2014-05-12 13:40:26 +00:00
else
2014-09-27 10:51:34 +00:00
t . data = fromHex ( tx [ " data " ] . get_str ( ) ) ;
else
for ( auto const & j : tx [ " data " ] . get_array ( ) )
t . data . push_back ( toByte ( j ) ) ;
callcreates . push_back ( t ) ;
2014-02-21 19:18:30 +00:00
}
2014-09-27 10:51:34 +00:00
}
2014-02-21 01:09:15 +00:00
2014-10-06 19:42:41 +00:00
h160 FakeState : : createNewAddress ( Address _newAddress , Address _sender , u256 _endowment , u256 _gasPrice , u256 * _gas , bytesConstRef _code , Address _origin , std : : set < Address > * o_suicides , Manifest * o_ms , OnOpFunc const & _onOp , unsigned _level )
{
if ( ! _origin )
_origin = _sender ;
if ( o_ms )
{
o_ms - > from = _sender ;
o_ms - > to = Address ( ) ;
o_ms - > value = _endowment ;
o_ms - > input = _code . toBytes ( ) ;
}
// Set up new account...
m_cache [ _newAddress ] = AddressState ( 0 , balance ( _newAddress ) + _endowment , h256 ( ) , h256 ( ) ) ;
// Execute init code.
VM vm ( * _gas ) ;
ExtVM evm ( * this , _newAddress , _sender , _origin , _endowment , _gasPrice , bytesConstRef ( ) , _code , o_ms , _level ) ;
bool revert = false ;
bytesConstRef out ;
try
{
out = vm . go ( evm , _onOp ) ;
if ( o_ms )
o_ms - > output = out . toBytes ( ) ;
if ( o_suicides )
for ( auto i : evm . suicides )
o_suicides - > insert ( i ) ;
}
catch ( OutOfGas const & /*_e*/ )
{
clog ( StateChat ) < < " Out of Gas! Reverting. " ;
revert = true ;
}
catch ( VMException const & _e )
{
clog ( StateChat ) < < " VM Exception: " < < diagnostic_information ( _e ) ;
}
catch ( Exception const & _e )
{
clog ( StateChat ) < < " Exception in VM: " < < diagnostic_information ( _e ) ;
}
catch ( std : : exception const & _e )
{
clog ( StateChat ) < < " std::exception in VM: " < < _e . what ( ) ;
}
// TODO: CHECK: IS THIS CORRECT?! (esp. given account created prior to revertion init.)
// Write state out only in the case of a non-out-of-gas transaction.
if ( revert )
evm . revert ( ) ;
// Set code.
if ( addressInUse ( _newAddress ) )
m_cache [ _newAddress ] . setCode ( out ) ;
* _gas = vm . gas ( ) ;
return _newAddress ;
}
2014-09-27 10:51:34 +00:00
namespace dev { namespace test {
2014-02-17 21:07:09 +00:00
2014-05-12 13:40:26 +00:00
void doTests ( json_spirit : : mValue & v , bool _fillin )
{
for ( auto & i : v . get_obj ( ) )
2014-02-21 19:18:30 +00:00
{
2014-05-12 13:40:26 +00:00
cnote < < i . first ;
mObject & o = i . second . get_obj ( ) ;
2014-02-21 19:18:30 +00:00
2014-05-12 13:40:26 +00:00
BOOST_REQUIRE ( o . count ( " env " ) > 0 ) ;
BOOST_REQUIRE ( o . count ( " pre " ) > 0 ) ;
BOOST_REQUIRE ( o . count ( " exec " ) > 0 ) ;
2014-04-20 05:09:41 +00:00
2014-09-05 15:09:58 +00:00
dev : : test : : FakeExtVM fev ;
2014-05-12 13:40:26 +00:00
fev . importEnv ( o [ " env " ] . get_obj ( ) ) ;
fev . importState ( o [ " pre " ] . get_obj ( ) ) ;
2014-02-21 19:18:30 +00:00
2014-05-12 13:40:26 +00:00
if ( _fillin )
o [ " pre " ] = mValue ( fev . exportState ( ) ) ;
2014-02-21 19:18:30 +00:00
2014-06-27 19:39:46 +00:00
fev . importExec ( o [ " exec " ] . get_obj ( ) ) ;
if ( ! fev . code )
2014-07-10 09:16:34 +00:00
{
fev . thisTxCode = get < 3 > ( fev . addresses . at ( fev . myAddress ) ) ;
fev . code = & fev . thisTxCode ;
}
2014-10-01 11:59:07 +00:00
2014-10-21 17:42:24 +00:00
auto argc = boost : : unit_test : : framework : : master_test_suite ( ) . argc ;
auto argv = boost : : unit_test : : framework : : master_test_suite ( ) . argv ;
auto useJit = argc > = 2 & & std : : string ( argv [ 1 ] ) = = " --jit " ;
2014-10-22 14:52:47 +00:00
auto vm = useJit ? std : : unique_ptr < VMFace > ( new jit : : VM ) : std : : unique_ptr < VMFace > ( new VM ) ;
vm - > reset ( fev . gas ) ;
2014-09-25 09:18:32 +00:00
bytes output ;
2014-10-21 17:42:24 +00:00
auto outOfGas = false ;
2014-09-25 09:18:32 +00:00
try
{
2014-10-22 14:52:47 +00:00
output = vm - > go ( fev ) . toVector ( ) ;
2014-10-21 12:44:09 +00:00
}
2014-10-21 17:42:24 +00:00
catch ( OutOfGas const & )
{
outOfGas = true ;
2014-09-25 09:18:32 +00:00
}
2014-10-02 12:20:33 +00:00
catch ( Exception const & _e )
2014-09-25 09:18:32 +00:00
{
2014-10-02 12:20:33 +00:00
cnote < < " VM did throw an exception: " < < diagnostic_information ( _e ) ;
}
catch ( std : : exception const & _e )
{
cnote < < " VM did throw an exception: " < < _e . what ( ) ;
2014-09-25 09:18:32 +00:00
}
2014-10-22 14:52:47 +00:00
auto gas = vm - > gas ( ) ;
2014-06-27 19:39:46 +00:00
2014-09-27 10:51:34 +00:00
// delete null entries in storage for the sake of comparison
for ( auto & a : fev . addresses )
{
vector < u256 > keystoDelete ;
for ( auto & s : get < 2 > ( a . second ) )
{
if ( s . second = = 0 )
keystoDelete . push_back ( s . first ) ;
}
for ( auto const key : keystoDelete )
{
get < 2 > ( a . second ) . erase ( key ) ;
}
}
2014-05-12 13:40:26 +00:00
if ( _fillin )
{
2014-07-10 10:29:39 +00:00
o [ " env " ] = mValue ( fev . exportEnv ( ) ) ;
2014-06-27 19:39:46 +00:00
o [ " exec " ] = mValue ( fev . exportExec ( ) ) ;
2014-05-12 13:40:26 +00:00
o [ " post " ] = mValue ( fev . exportState ( ) ) ;
o [ " callcreates " ] = fev . exportCallCreates ( ) ;
2014-07-14 15:24:07 +00:00
o [ " out " ] = " 0x " + toHex ( output ) ;
2014-10-16 16:20:49 +00:00
fev . push ( o , " gas " , gas ) ;
2014-05-12 13:40:26 +00:00
}
else
{
BOOST_REQUIRE ( o . count ( " post " ) > 0 ) ;
BOOST_REQUIRE ( o . count ( " callcreates " ) > 0 ) ;
BOOST_REQUIRE ( o . count ( " out " ) > 0 ) ;
BOOST_REQUIRE ( o . count ( " gas " ) > 0 ) ;
2014-09-05 15:09:58 +00:00
dev : : test : : FakeExtVM test ;
2014-05-12 13:40:26 +00:00
test . importState ( o [ " post " ] . get_obj ( ) ) ;
test . importCallCreates ( o [ " callcreates " ] . get_array ( ) ) ;
int i = 0 ;
2014-07-14 15:24:07 +00:00
if ( o [ " out " ] . type ( ) = = array_type )
for ( auto const & d : o [ " out " ] . get_array ( ) )
{
2014-09-27 10:51:34 +00:00
BOOST_CHECK_MESSAGE ( output [ i ] = = test . toInt ( d ) , " Output byte [ " < < i < < " ] different! " ) ;
2014-07-14 15:24:07 +00:00
+ + i ;
}
else if ( o [ " out " ] . get_str ( ) . find ( " 0x " ) = = 0 )
BOOST_CHECK ( output = = fromHex ( o [ " out " ] . get_str ( ) . substr ( 2 ) ) ) ;
else
BOOST_CHECK ( output = = fromHex ( o [ " out " ] . get_str ( ) ) ) ;
2014-10-21 09:54:54 +00:00
BOOST_CHECK_EQUAL ( test . toInt ( o [ " gas " ] ) , gas ) ;
2014-10-21 17:42:24 +00:00
if ( outOfGas )
BOOST_CHECK_MESSAGE ( gas = = 0 , " Remaining gas not 0 in out-of-gas state " ) ;
2014-10-21 09:54:54 +00:00
auto & expectedAddrs = test . addresses ;
auto & resultAddrs = fev . addresses ;
for ( auto & & expectedPair : expectedAddrs )
{
auto & expectedAddr = expectedPair . first ;
auto resultAddrIt = resultAddrs . find ( expectedAddr ) ;
if ( resultAddrIt = = resultAddrs . end ( ) )
BOOST_ERROR ( " Missing expected address " < < expectedAddr ) ;
else
{
auto & expectedState = expectedPair . second ;
auto & resultState = resultAddrIt - > second ;
BOOST_CHECK_MESSAGE ( std : : get < 0 > ( expectedState ) = = std : : get < 0 > ( resultState ) , expectedAddr < < " : incorrect balance " < < std : : get < 0 > ( resultState ) < < " , expected " < < std : : get < 0 > ( expectedState ) ) ;
BOOST_CHECK_MESSAGE ( std : : get < 1 > ( expectedState ) = = std : : get < 1 > ( resultState ) , expectedAddr < < " : incorrect txCount " < < std : : get < 1 > ( resultState ) < < " , expected " < < std : : get < 1 > ( expectedState ) ) ;
BOOST_CHECK_MESSAGE ( std : : get < 3 > ( expectedState ) = = std : : get < 3 > ( resultState ) , expectedAddr < < " : incorrect code " ) ;
auto & & expectedStore = std : : get < 2 > ( expectedState ) ;
auto & & resultStore = std : : get < 2 > ( resultState ) ;
for ( auto & & expectedStorePair : expectedStore )
{
auto & expectedStoreKey = expectedStorePair . first ;
auto resultStoreIt = resultStore . find ( expectedStoreKey ) ;
if ( resultStoreIt = = resultStore . end ( ) )
BOOST_ERROR ( expectedAddr < < " : missing store key " < < expectedStoreKey ) ;
else
{
auto & expectedStoreValue = expectedStorePair . second ;
auto & resultStoreValue = resultStoreIt - > second ;
BOOST_CHECK_MESSAGE ( expectedStoreValue = = resultStoreValue , expectedAddr < < " : store[ " < < expectedStoreKey < < " ] = " < < resultStoreValue < < " , expected " < < expectedStoreValue ) ;
}
}
}
}
BOOST_CHECK ( test . addresses = = fev . addresses ) ; // Just to make sure nothing missed
2014-05-12 13:40:26 +00:00
BOOST_CHECK ( test . callcreates = = fev . callcreates ) ;
2014-02-21 19:18:30 +00:00
}
}
2014-05-12 13:40:26 +00:00
}
/*string makeTestCase()
{
json_spirit : : mObject o ;
VM vm ;
BlockInfo pb ;
pb . hash = sha3 ( " previousHash " ) ;
pb . nonce = sha3 ( " previousNonce " ) ;
BlockInfo cb = pb ;
cb . difficulty = 256 ;
cb . timestamp = 1 ;
cb . coinbaseAddress = toAddress ( sha3 ( " coinbase " ) ) ;
FakeExtVM fev ( pb , cb , 0 ) ;
bytes init ;
fev . setContract ( toAddress ( sha3 ( " contract " ) ) , ether , 0 , compileLisp ( " (suicide (txsender)) " , false , init ) , map < u256 , u256 > ( ) ) ;
o [ " env " ] = fev . exportEnv ( ) ;
o [ " pre " ] = fev . exportState ( ) ;
fev . setTransaction ( toAddress ( sha3 ( " sender " ) ) , ether , finney , bytes ( ) ) ;
mArray execs ;
execs . push_back ( fev . exportExec ( ) ) ;
o [ " exec " ] = execs ;
vm . go ( fev ) ;
o [ " post " ] = fev . exportState ( ) ;
o [ " txs " ] = fev . exportTxs ( ) ;
return json_spirit : : write_string ( json_spirit : : mValue ( o ) , true ) ;
} */
2014-10-01 11:59:07 +00:00
void executeTests ( const string & _name )
{
2014-10-14 13:13:38 +00:00
const char * ptestPath = getenv ( " ETHEREUM_TEST_PATH " ) ;
string testPath ;
if ( ptestPath = = NULL )
2014-10-14 10:41:01 +00:00
{
cnote < < " could not find environment variable ETHEREUM_TEST_PATH \n " ;
testPath = " ../../../tests/vmtests " ;
}
2014-10-14 13:13:38 +00:00
else
testPath = ptestPath ;
2014-10-14 10:41:01 +00:00
2014-09-25 09:18:32 +00:00
# ifdef FILL_TESTS
2014-09-20 20:18:42 +00:00
try
{
cnote < < " Populating VM tests... " ;
json_spirit : : mValue v ;
2014-10-14 10:41:01 +00:00
boost : : filesystem : : path p ( __FILE__ ) ;
boost : : filesystem : : path dir = p . parent_path ( ) ;
string s = asString ( contents ( dir . string ( ) + " / " + _name + " Filler.json " ) ) ;
2014-09-25 09:18:32 +00:00
BOOST_REQUIRE_MESSAGE ( s . length ( ) > 0 , " Contents of " + _name + " Filler.json is empty. " ) ;
2014-09-20 20:18:42 +00:00
json_spirit : : read_string ( s , v ) ;
dev : : test : : doTests ( v , true ) ;
2014-10-14 13:13:38 +00:00
writeFile ( testPath + " / " + _name + " .json " , asBytes ( json_spirit : : write_string ( v , true ) ) ) ;
2014-09-20 20:18:42 +00:00
}
2014-10-02 12:20:33 +00:00
catch ( Exception const & _e )
2014-05-12 13:40:26 +00:00
{
2014-10-02 12:20:33 +00:00
BOOST_ERROR ( " Failed VM Test with Exception: " < < diagnostic_information ( _e ) ) ;
}
catch ( std : : exception const & _e )
{
BOOST_ERROR ( " Failed VM Test with Exception: " < < _e . what ( ) ) ;
2014-09-25 09:18:32 +00:00
}
# endif
2014-07-06 14:16:53 +00:00
2014-04-19 17:55:20 +00:00
try
{
2014-09-25 09:18:32 +00:00
cnote < < " Testing VM... " < < _name ;
2014-04-19 17:55:20 +00:00
json_spirit : : mValue v ;
2014-10-14 13:13:38 +00:00
string s = asString ( contents ( testPath + " / " + _name + " .json " ) ) ;
BOOST_REQUIRE_MESSAGE ( s . length ( ) > 0 , " Contents of " + _name + " .json is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path? " ) ;
2014-04-19 17:55:20 +00:00
json_spirit : : read_string ( s , v ) ;
2014-09-05 15:09:58 +00:00
dev : : test : : doTests ( v , false ) ;
2014-04-19 17:55:20 +00:00
}
2014-10-02 12:20:33 +00:00
catch ( Exception const & _e )
{
BOOST_ERROR ( " Failed VM Test with Exception: " < < diagnostic_information ( _e ) ) ;
}
catch ( std : : exception const & _e )
2014-04-19 17:55:20 +00:00
{
2014-10-02 12:20:33 +00:00
BOOST_ERROR ( " Failed VM Test with Exception: " < < _e . what ( ) ) ;
2014-04-19 17:55:20 +00:00
}
2014-09-25 09:18:32 +00:00
}
} } // Namespace Close
BOOST_AUTO_TEST_CASE ( vm_tests )
{
dev : : test : : executeTests ( " vmtests " ) ;
2014-04-19 17:55:20 +00:00
}
2014-09-20 00:05:04 +00:00
BOOST_AUTO_TEST_CASE ( vmArithmeticTest )
{
2014-09-25 09:18:32 +00:00
dev : : test : : executeTests ( " vmArithmeticTest " ) ;
}
2014-09-20 00:05:04 +00:00
2014-09-25 09:18:32 +00:00
BOOST_AUTO_TEST_CASE ( vmBitwiseLogicOperationTest )
{
dev : : test : : executeTests ( " vmBitwiseLogicOperationTest " ) ;
}
BOOST_AUTO_TEST_CASE ( vmSha3Test )
{
dev : : test : : executeTests ( " vmSha3Test " ) ;
2014-09-20 00:05:04 +00:00
}
2014-09-25 09:18:32 +00:00
BOOST_AUTO_TEST_CASE ( vmEnvironmentalInfoTest )
{
dev : : test : : executeTests ( " vmEnvironmentalInfoTest " ) ;
}
BOOST_AUTO_TEST_CASE ( vmBlockInfoTest )
{
dev : : test : : executeTests ( " vmBlockInfoTest " ) ;
}
BOOST_AUTO_TEST_CASE ( vmIOandFlowOperationsTest )
{
dev : : test : : executeTests ( " vmIOandFlowOperationsTest " ) ;
}
BOOST_AUTO_TEST_CASE ( vmPushDupSwapTest )
{
dev : : test : : executeTests ( " vmPushDupSwapTest " ) ;
}
2014-09-27 10:51:34 +00:00
BOOST_AUTO_TEST_CASE ( vmSystemOperationsTest )
{
dev : : test : : executeTests ( " vmSystemOperationsTest " ) ;
}
2014-10-06 19:42:41 +00:00