@ -20,11 +20,15 @@
* State test functions .
*/
# include <fstream>
# include "../json_spirit/json_spirit_reader_template.h"
# include "../json_spirit/json_spirit_writer_template.h"
# include <ExtVMFace.h>
# include <Transaction.h>
# include <VM.h>
# include <Instruction.h>
using namespace std ;
using namespace json_spirit ;
using namespace eth ;
namespace eth
@ -33,37 +37,236 @@ namespace eth
class FakeExtVM : public ExtVMFace
{
public :
FakeExtVM ( Address _myAddress , u256 _myBalance , u256 _myNonce , u256s _myData , Address _txSender , u256 _txValue , u256s const & _txData , FeeStructure const & _fees , BlockInfo const & _previousBlock , BlockInfo const & _currentBlock , uint _currentNumber ) :
ExtVMFace ( _myAddress , _txSender , _txValue , _txData , _fees , _previousBlock , _currentBlock , _currentNumber )
{
reset ( _myBalance , _myNonce , _myData ) ;
}
FakeExtVM ( )
{ }
FakeExtVM ( FeeStructure const & _fees , BlockInfo const & _previousBlock , BlockInfo const & _currentBlock , uint _currentNumber ) :
ExtVMFace ( Address ( ) , Address ( ) , 0 , u256s ( ) , _fees , _previousBlock , _currentBlock , _currentNumber )
{ }
u256 store ( u256 _n ) { return get < 3 > ( addresses [ myAddress ] ) [ _n ] ; }
void setStore ( u256 _n , u256 _v ) { get < 3 > ( addresses [ myAddress ] ) [ _n ] = _v ; }
void mktx ( Transaction & _t ) { txs . push_back ( _t ) ; }
void mktx ( Transaction & _t )
{
if ( get < 0 > ( addresses [ myAddress ] ) > = _t . value )
{
get < 0 > ( addresses [ myAddress ] ) - = _t . value ;
get < 1 > ( addresses [ myAddress ] ) + + ;
// get<0>(addresses[_t.receiveAddress]) += _t.value;
txs . push_back ( _t ) ;
}
}
u256 balance ( Address _a ) { return get < 0 > ( addresses [ _a ] ) ; }
void payFee ( bigint _fee ) { get < 0 > ( addresses [ myAddress ] ) = ( u256 ) ( get < 0 > ( addresses [ myAddress ] ) - _fee ) ; }
u256 txCount ( Address _a ) { return get < 1 > ( addresses [ _a ] ) ; }
u256 extro ( Address _a , u256 _pos ) { return get < 3 > ( addresses [ _a ] ) [ _pos ] ; }
u256 extroPrice ( Address _a ) { return get < 2 > ( addresses [ _a ] ) ; }
void suicide ( Address _a ) { dead = _a ; }
void suicide ( Address _a )
{
for ( auto const & i : get < 3 > ( addresses [ myAddress ] ) )
if ( i . second )
get < 0 > ( addresses [ _a ] ) + = fees . m_memoryFee ;
get < 0 > ( addresses [ _a ] ) + = get < 0 > ( addresses [ myAddress ] ) ;
addresses . erase ( myAddress ) ;
}
void setTransaction ( Address _txSender , u256 _txValue , u256s const & _txData )
{
txSender = _txSender ;
txValue = _txValue ;
txData = _txData ;
}
void setContract ( Address _myAddress , u256 _myBalance , u256 _myNonce , u256s _myData )
{
myAddress = _myAddress ;
set ( myAddress , _myBalance , _myNonce , _myData ) ;
}
void set ( Address _a , u256 _myBalance , u256 _myNonce , u256s _myData )
{
get < 0 > ( addresses [ _a ] ) = _myBalance ;
get < 1 > ( addresses [ _a ] ) = _myNonce ;
get < 2 > ( addresses [ _a ] ) = 0 ;
for ( unsigned i = 0 ; i < _myData . size ( ) ; + + i )
get < 3 > ( addresses [ _a ] ) [ i ] = _myData [ i ] ;
}
mObject exportEnv ( )
{
mObject ret ;
ret [ " previousHash " ] = toString ( previousBlock . hash ) ;
ret [ " previousNonce " ] = toString ( previousBlock . nonce ) ;
push ( ret , " currentDifficulty " , currentBlock . difficulty ) ;
push ( ret , " currentTimestamp " , currentBlock . timestamp ) ;
ret [ " currentCoinbase " ] = toString ( currentBlock . coinbaseAddress ) ;
push ( ret , " feeMultiplier " , fees . multiplier ( ) ) ;
return ret ;
}
void importEnv ( mObject & _o )
{
previousBlock . hash = h256 ( _o [ " previousHash " ] . get_str ( ) ) ;
previousBlock . nonce = h256 ( _o [ " previousNonce " ] . get_str ( ) ) ;
currentBlock . difficulty = toInt ( _o [ " currentDifficulty " ] ) ;
currentBlock . timestamp = toInt ( _o [ " currentTimestamp " ] ) ;
currentBlock . coinbaseAddress = Address ( _o [ " currentCoinbase " ] . get_str ( ) ) ;
fees . setMultiplier ( toInt ( _o [ " feeMultiplier " ] ) ) ;
}
static u256 toInt ( mValue const & _v )
{
switch ( _v . type ( ) )
{
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 ( ) ;
}
return 0 ;
}
static void push ( mObject & o , string const & _n , u256 _v )
{
if ( _v < ( u256 ) 1 < < 64 )
o [ _n ] = ( uint64_t ) _v ;
else
o [ _n ] = toString ( _v ) ;
}
static void push ( mArray & a , u256 _v )
{
if ( _v < ( u256 ) 1 < < 64 )
a . push_back ( ( uint64_t ) _v ) ;
else
a . push_back ( toString ( _v ) ) ;
}
mObject exportState ( )
{
mObject ret ;
for ( auto const & a : addresses )
{
mObject o ;
push ( o , " balance " , get < 0 > ( a . second ) ) ;
push ( o , " nonce " , get < 1 > ( a . second ) ) ;
push ( o , " extroPrice " , get < 2 > ( a . second ) ) ;
mObject store ;
string curKey ;
u256 li = 0 ;
mArray curVal ;
for ( auto const & s : get < 3 > ( a . second ) )
{
if ( ! li | | s . first > li + 8 )
{
if ( li )
store [ curKey ] = curVal ;
li = s . first ;
curKey = toString ( li ) ;
curVal = mArray ( ) ;
}
else
for ( ; li ! = s . first ; + + li )
curVal . push_back ( 0 ) ;
push ( curVal , s . second ) ;
+ + li ;
}
if ( li )
{
store [ curKey ] = curVal ;
o [ " store " ] = store ;
}
ret [ toString ( a . first ) ] = o ;
}
return ret ;
}
void importState ( mObject & _o )
{
for ( auto const & i : _o )
{
mObject o = i . second . get_obj ( ) ;
auto & a = addresses [ Address ( i . first ) ] ;
get < 0 > ( a ) = toInt ( o [ " balance " ] ) ;
get < 1 > ( a ) = toInt ( o [ " nonce " ] ) ;
get < 2 > ( a ) = toInt ( o [ " extroPrice " ] ) ;
if ( o . count ( " store " ) )
for ( auto const & j : o [ " store " ] . get_obj ( ) )
{
u256 adr ( j . first ) ;
for ( auto const & k : j . second . get_array ( ) )
get < 3 > ( a ) [ adr + + ] = toInt ( k ) ;
}
if ( o . count ( " code " ) )
{
u256s d = compileLisp ( o [ " code " ] . get_str ( ) ) ;
for ( unsigned i = 0 ; i < d . size ( ) ; + + i )
get < 3 > ( a ) [ ( u256 ) i ] = d [ i ] ;
}
}
}
mObject exportExec ( )
{
mObject ret ;
ret [ " address " ] = toString ( myAddress ) ;
ret [ " sender " ] = toString ( txSender ) ;
push ( ret , " value " , txValue ) ;
mArray d ;
for ( auto const & i : txData )
push ( d , i ) ;
ret [ " data " ] = d ;
return ret ;
}
void importExec ( mObject & _o )
{
myAddress = Address ( _o [ " address " ] . get_str ( ) ) ;
txSender = Address ( _o [ " sender " ] . get_str ( ) ) ;
txValue = toInt ( _o [ " value " ] ) ;
for ( auto const & j : _o [ " data " ] . get_array ( ) )
txData . push_back ( toInt ( j ) ) ;
}
mArray exportTxs ( )
{
mArray ret ;
for ( Transaction const & tx : txs )
{
mObject o ;
o [ " destination " ] = toString ( tx . receiveAddress ) ;
push ( o , " value " , tx . value ) ;
mArray d ;
for ( auto const & i : tx . data )
push ( d , i ) ;
o [ " data " ] = d ;
ret . push_back ( o ) ;
}
return ret ;
}
void importTxs ( mArray & _txs )
{
for ( mValue & v : _txs )
{
auto tx = v . get_obj ( ) ;
Transaction t ;
t . receiveAddress = Address ( tx [ " destination " ] . get_str ( ) ) ;
t . value = toInt ( tx [ " value " ] ) ;
for ( auto const & j : tx [ " data " ] . get_array ( ) )
t . data . push_back ( toInt ( j ) ) ;
txs . push_back ( t ) ;
}
}
void reset ( u256 _myBalance , u256 _myNonce , u256s _myData )
{
txs . clear ( ) ;
addresses . clear ( ) ;
get < 0 > ( addresses [ myAddress ] ) = _myBalance ;
get < 1 > ( addresses [ myAddress ] ) = _myNonce ;
get < 2 > ( addresses [ myAddress ] ) = 0 ;
for ( unsigned i = 0 ; i < _myData . size ( ) ; + + i )
get < 3 > ( addresses [ myAddress ] ) [ i ] = _myData [ i ] ;
dead = Address ( ) ;
set ( myAddress , _myBalance , _myNonce , _myData ) ;
}
map < Address , tuple < u256 , u256 , u256 , map < u256 , u256 > > > addresses ;
Transactions txs ;
Address dead ;
} ;
template < > class UnitTest < 1 >
@ -71,6 +274,72 @@ template <> class UnitTest<1>
public :
int operator ( ) ( )
{
json_spirit : : mValue v ;
string s = asString ( contents ( " /home/gav/Projects/cpp-ethereum/test/vmtests.json " ) ) ;
cout < < s < < endl ;
json_spirit : : read_string ( s , v ) ;
doTests ( v , true ) ;
cout < < json_spirit : : write_string ( v , true ) < < endl ;
bool passed = doTests ( v , false ) ;
return passed ? 0 : 1 ;
}
bool doTests ( json_spirit : : mValue & v , bool _fillin )
{
bool passed = true ;
for ( auto & i : v . get_obj ( ) )
{
cnote < < i . first ;
mObject & o = i . second . get_obj ( ) ;
VM vm ;
FakeExtVM fev ;
fev . importEnv ( o [ " env " ] . get_obj ( ) ) ;
fev . importState ( o [ " pre " ] . get_obj ( ) ) ;
if ( _fillin )
o [ " pre " ] = mValue ( fev . exportState ( ) ) ;
for ( auto i : o [ " exec " ] . get_array ( ) )
{
fev . importExec ( i . get_obj ( ) ) ;
vm . go ( fev ) ;
}
if ( _fillin )
{
o [ " post " ] = mValue ( fev . exportState ( ) ) ;
o [ " txs " ] = fev . exportTxs ( ) ;
}
else
{
FakeExtVM test ;
test . importState ( o [ " post " ] . get_obj ( ) ) ;
test . importTxs ( o [ " txs " ] . get_array ( ) ) ;
if ( test . addresses ! = fev . addresses )
{
cwarn < < " Test failed: state different. " ;
passed = false ;
}
if ( test . txs ! = fev . txs )
{
cwarn < < " Test failed: tx list different: " ;
cwarn < < test . txs ;
cwarn < < fev . txs ;
passed = false ;
}
}
}
return passed ;
}
string makeTestCase ( )
{
json_spirit : : mObject o ;
VM vm ;
BlockInfo pb ;
pb . hash = sha3 ( " previousHash " ) ;
@ -81,15 +350,19 @@ public:
cb . coinbaseAddress = toAddress ( sha3 ( " coinbase " ) ) ;
FeeStructure fees ;
fees . setMultiplier ( 1 ) ;
string code = " (suicide (txsender)) " ;
FakeExtVM fev ( toAddress ( sha3 ( " contract " ) ) , ether , 0 , compileLisp ( code ) , toAddress ( sha3 ( " sender " ) ) , ether , u256s ( ) , fees , pb , cb , 0 ) ;
FakeExtVM fev ( fees , pb , cb , 0 ) ;
fev . setContract ( toAddress ( sha3 ( " contract " ) ) , ether , 0 , compileLisp ( " (suicide (txsender)) " ) ) ;
o [ " env " ] = fev . exportEnv ( ) ;
o [ " pre " ] = fev . exportState ( ) ;
fev . setTransaction ( toAddress ( sha3 ( " sender " ) ) , ether , u256s ( ) ) ;
mArray execs ;
execs . push_back ( fev . exportExec ( ) ) ;
o [ " exec " ] = execs ;
vm . go ( fev ) ;
cnote < < fev . dead < < formatBalance ( fev . balance ( toAddress ( sha3 ( " contract " ) ) ) ) ;
o [ " post " ] = fev . exportState ( ) ;
o [ " txs " ] = fev . exportTxs ( ) ;
return 0 ;
return json_spirit : : write_string ( json_spirit : : mValue ( o ) , true ) ;
}
} ;