@ -25,6 +25,7 @@
# include "Transact.h"
# include <fstream>
# include <boost/algorithm/string.hpp>
# include <QFileDialog>
# include <QMessageBox>
# include <liblll/Compiler.h>
@ -135,21 +136,6 @@ void Transact::updateFee()
ui - > total - > setPalette ( p ) ;
}
string Transact : : getFunctionHashes ( dev : : solidity : : CompilerStack const & _compiler , string const & _contractName )
{
string ret = " " ;
auto const & contract = _compiler . getContractDefinition ( _contractName ) ;
auto interfaceFunctions = contract . getInterfaceFunctions ( ) ;
for ( auto const & it : interfaceFunctions )
{
ret + = it . first . abridged ( ) ;
ret + = " : " ;
ret + = it . second - > getDeclaration ( ) . getName ( ) + " \n " ;
}
return ret ;
}
void Transact : : on_destination_currentTextChanged ( QString )
{
if ( ui - > destination - > currentText ( ) . size ( ) & & ui - > destination - > currentText ( ) ! = " (Create Contract) " )
@ -182,202 +168,222 @@ static std::string toString(TransactionException _te)
}
}
void Transact : : rejigData ( )
static string getFunctionHashes ( dev : : solidity : : CompilerStack const & _compiler , string const & _contractName )
{
if ( ! ethereum ( ) )
return ;
m_allGood = true ;
if ( isCreation ( ) )
string ret = " " ;
auto const & contract = _compiler . getContractDefinition ( _contractName ) ;
auto interfaceFunctions = contract . getInterfaceFunctions ( ) ;
for ( auto const & it : interfaceFunctions )
{
string src = ui - > data - > toPlainText ( ) . toStdString ( ) ;
vector < string > errors ;
QString lll ;
QString solidity ;
if ( src . find_first_not_of ( " 1234567890abcdefABCDEF " ) = = string : : npos & & src . size ( ) % 2 = = 0 )
m_data = fromHex ( src ) ;
else if ( sourceIsSolidity ( src ) )
ret + = it . first . abridged ( ) ;
ret + = " : " ;
ret + = it . second - > getDeclaration ( ) . getName ( ) + " \n " ;
}
return ret ;
}
static tuple < vector < string > , bytes , string > userInputToCode ( string const & _user , bool _opt )
{
string lll ;
string solidity ;
bytes data ;
vector < string > errors ;
if ( _user . find_first_not_of ( " 1234567890abcdefABCDEF \n \t " ) = = string : : npos & & _user . size ( ) % 2 = = 0 )
{
std : : string u = _user ;
boost : : replace_all_copy ( u , " \n " , " " ) ;
boost : : replace_all_copy ( u , " \t " , " " ) ;
boost : : replace_all_copy ( u , " " , " " ) ;
data = fromHex ( u ) ;
}
else if ( sourceIsSolidity ( _user ) )
{
dev : : solidity : : CompilerStack compiler ( true ) ;
try
{
dev : : solidity : : CompilerStack compiler ( true ) ;
try
{
// compiler.addSources(dev::solidity::StandardSources);
m_data = compiler . compile ( src , ui - > optimize - > isChecked ( ) ) ;
solidity = " <h4>Solidity</h4> " ;
solidity + = " <pre>var " + QString : : fromStdString ( compiler . defaultContractName ( ) ) + " = web3.eth.contractFromAbi( " + QString : : fromStdString ( compiler . getInterface ( ) ) . replace ( QRegExp ( " \\ s " ) , " " ) . toHtmlEscaped ( ) + " );</pre> " ;
solidity + = " <pre> " + QString : : fromStdString ( compiler . getSolidityInterface ( ) ) . toHtmlEscaped ( ) + " </pre> " ;
solidity + = " <pre> " + QString : : fromStdString ( getFunctionHashes ( compiler ) ) . toHtmlEscaped ( ) + " </pre> " ;
}
catch ( dev : : Exception const & exception )
{
ostringstream error ;
solidity : : SourceReferenceFormatter : : printExceptionInformation ( error , exception , " Error " , compiler ) ;
errors . push_back ( " Solidity: " + error . str ( ) ) ;
}
catch ( . . . )
{
errors . push_back ( " Solidity: Uncaught exception " ) ;
}
data = compiler . compile ( _user , _opt ) ;
solidity = " <h4>Solidity</h4> " ;
solidity + = " <pre>var " + compiler . defaultContractName ( ) + " = web3.eth.contract( " + QString : : fromStdString ( compiler . getInterface ( ) ) . replace ( QRegExp ( " \\ s " ) , " " ) . toHtmlEscaped ( ) . toStdString ( ) + " );</pre> " ;
solidity + = " <pre> " + QString : : fromStdString ( compiler . getSolidityInterface ( ) ) . toHtmlEscaped ( ) . toStdString ( ) + " </pre> " ;
solidity + = " <pre> " + QString : : fromStdString ( getFunctionHashes ( compiler , " " ) ) . toHtmlEscaped ( ) . toStdString ( ) + " </pre> " ;
}
catch ( dev : : Exception const & exception )
{
ostringstream error ;
solidity : : SourceReferenceFormatter : : printExceptionInformation ( error , exception , " Error " , compiler ) ;
errors . push_back ( " Solidity: " + error . str ( ) ) ;
}
catch ( . . . )
{
errors . push_back ( " Solidity: Uncaught exception " ) ;
}
}
# ifndef _MSC_VER
else if ( sourceIsSerpent ( src ) )
else if ( sourceIsSerpent ( _user ) )
{
try
{
try
{
m_data = dev : : asBytes ( : : compile ( src ) ) ;
}
catch ( string const & err )
{
errors . push_back ( " Serpent " + err ) ;
}
data = dev : : asBytes ( : : compile ( _user ) ) ;
}
# endif
else
catch ( string const & err )
{
m_data = compileLLL ( src , ui - > optimize - > isChecked ( ) , & errors ) ;
if ( errors . empty ( ) )
{
auto asmcode = compileLLLToAsm ( src , ui - > optimize - > isChecked ( ) ) ;
lll = " <h4>LLL</h4><pre> " + QString : : fromStdString ( asmcode ) . toHtmlEscaped ( ) + " </pre> " ;
}
errors . push_back ( " Serpent " + err ) ;
}
QString htmlErrors ;
qint64 gasNeeded = 0 ;
if ( errors . size ( ) )
}
# endif
else
{
data = compileLLL ( _user , _opt , & errors ) ;
if ( errors . empty ( ) )
{
htmlErrors = " <h4>Errors</h4> " ;
for ( auto const & i : errors )
htmlErrors . append ( " <div style= \" border-left: 6px solid #c00; margin-top: 2px \" > " + QString : : fromStdString ( i ) . toHtmlEscaped ( ) + " </div> " ) ;
m_allGood = false ;
auto asmcode = compileLLLToAsm ( _user , _opt ) ;
lll = " <h4>LLL</h4><pre> " + QString : : fromStdString ( asmcode ) . toHtmlEscaped ( ) . toStdString ( ) + " </pre> " ;
}
}
return make_tuple ( errors , data , lll + solidity ) ;
}
string Transact : : natspecNotice ( Address _to , bytes const & _data )
{
if ( ethereum ( ) - > codeAt ( _to , PendingBlock ) . size ( ) )
{
string userNotice = m_natSpecDB - > getUserNotice ( ethereum ( ) - > postState ( ) . codeHash ( _to ) , _data ) ;
if ( userNotice . empty ( ) )
return " Destination contract unknown. " ;
else
{
if ( true )
{
auto s = findSecret ( value ( ) + ethereum ( ) - > gasLimitRemaining ( ) * gasPrice ( ) ) ;
if ( ! s )
htmlErrors + = " <div class= \" error \" ><span class= \" icon \" >WARNING</span> No single account contains a comfortable amount of gas.</div> " ;
// TODO: use account with most balance anyway.
else
{
ExecutionResult er = ethereum ( ) - > create ( s , value ( ) , m_data , ethereum ( ) - > gasLimitRemaining ( ) , gasPrice ( ) ) ;
gasNeeded = ( qint64 ) er . gasUsed ;
auto base = ( qint64 ) Interface : : txGas ( m_data , 0 ) ;
htmlErrors + = QString ( " <div class= \" info \" ><span class= \" icon \" >INFO</span> Gas required: %1 base, %2 init</div> " ) . arg ( base ) . arg ( ( qint64 ) er . gasUsed - base ) ;
if ( er . excepted ! = TransactionException : : None )
{
htmlErrors + = " <div class= \" error \" ><span class= \" icon \" >ERROR</span> " + QString : : fromStdString ( toString ( er . excepted ) ) + " </div> " ;
m_allGood = false ;
}
if ( er . codeDeposit = = CodeDeposit : : Failed )
{
htmlErrors + = " <div class= \" error \" ><span class= \" icon \" >ERROR</span> Code deposit failed due to insufficient gas</div> " ;
m_allGood = false ;
}
}
}
else
gasNeeded = ( qint64 ) Interface : : txGas ( m_data , 0 ) ;
NatspecExpressionEvaluator evaluator ;
return evaluator . evalExpression ( QString : : fromStdString ( userNotice ) ) . toStdString ( ) ;
}
}
else
return " Destination not a contract. " ;
}
void Transact : : rejigData ( )
{
if ( ! ethereum ( ) )
return ;
ui - > code - > setHtml ( htmlErrors + lll + solidity + " <h4>Code</h4> " + QString : : fromStdString ( disassemble ( m_data ) ) . toHtmlEscaped ( ) + " <h4>Hex</h4> " Div ( Mono ) + QString : : fromStdString ( toHex ( m_data ) ) + " </div> " ) ;
// Determine how much balance we have to play with...
auto s = findSecret ( value ( ) + ethereum ( ) - > gasLimitRemaining ( ) * gasPrice ( ) ) ;
auto b = ethereum ( ) - > balanceAt ( KeyPair ( s ) . address ( ) , PendingBlock ) ;
m_allGood = true ;
QString htmlInfo ;
if ( ui - > gas - > value ( ) = = ui - > gas - > minimum ( ) )
auto bail = [ & ] ( QString he ) {
m_allGood = false ;
ui - > send - > setEnabled ( false ) ;
ui - > code - > setHtml ( he + htmlInfo ) ;
} ;
// Determine m_info.
if ( isCreation ( ) )
{
string info ;
vector < string > errors ;
tie ( errors , m_data , info ) = userInputToCode ( ui - > data - > toPlainText ( ) . toStdString ( ) , ui - > optimize - > isChecked ( ) ) ;
if ( errors . size ( ) )
{
ui - > gas - > setMinimum ( gasNeeded ) ;
ui - > gas - > setValue ( gasNeeded ) ;
// Errors determining transaction data (i.e. init code). Bail.
QString htmlErrors ;
for ( auto const & i : errors )
htmlErrors . append ( " <div class= \" error \" ><span class= \" icon \" >ERROR</span> " + QString : : fromStdString ( i ) . toHtmlEscaped ( ) + " </div> " ) ;
bail ( htmlErrors ) ;
return ;
}
else
ui - > gas - > setMinimum ( gasNeeded ) ;
// if (!ui->gas->isEnabled())
// ui->gas->setValue(m_backupGas);
ui - > gas - > setEnabled ( true ) ;
// if (ui->gas->value() == ui->gas->minimum() && !src.empty())
// ui->gas->setValue((int)(m_ethereum->postState().gasLimitRemaining() / 10));
htmlInfo = QString : : fromStdString ( info ) + " <h4>Code</h4> " + QString : : fromStdString ( disassemble ( m_data ) ) . toHtmlEscaped ( ) ;
}
else
{
m_allGood = true ;
auto base = ( qint64 ) Interface : : txGas ( m_data , 0 ) ;
m_data = parseData ( ui - > data - > toPlainText ( ) . toStdString ( ) ) ;
auto to = m_context - > fromString ( ui - > destination - > currentText ( ) ) ;
QString natspec ;
QString errs ;
if ( ethereum ( ) - > codeAt ( to , PendingBlock ) . size ( ) )
{
string userNotice = m_natSpecDB - > getUserNotice ( ethereum ( ) - > postState ( ) . codeHash ( to ) , m_data ) ;
if ( userNotice . empty ( ) )
natspec = " Destination contract unknown. " ;
else
{
NatspecExpressionEvaluator evaluator ;
natspec = evaluator . evalExpression ( QString : : fromStdString ( userNotice ) ) ;
}
htmlInfo = " <h4>Dump</h4> " + QString : : fromStdString ( dev : : memDump ( m_data , 8 , true ) ) ;
}
qint64 gasNeeded = 0 ;
if ( true )
{
auto s = findSecret ( value ( ) + ethereum ( ) - > gasLimitRemaining ( ) * gasPrice ( ) ) ;
if ( ! s )
errs + = " <div class= \" error \" ><span class= \" icon \" >ERROR</span> No single account contains enough gas.</div> " ;
// TODO: use account with most balance anyway.
else
{
ExecutionResult er = ethereum ( ) - > call ( s , value ( ) , to , m_data , ethereum ( ) - > gasLimitRemaining ( ) , gasPrice ( ) ) ;
gasNeeded = ( qint64 ) er . gasUsed ;
errs + = QString ( " <div class= \" info \" ><span class= \" icon \" >INFO</span> Gas required: %1 base, %2 exec</div> " ) . arg ( base ) . arg ( ( qint64 ) er . gasUsed - base ) ;
if ( er . excepted ! = TransactionException : : None )
{
errs + = " <div class= \" error \" ><span class= \" icon \" >ERROR</span> " + QString : : fromStdString ( toString ( er . excepted ) ) + " </div> " ;
m_allGood = false ;
}
}
}
else
gasNeeded = ( qint64 ) Interface : : txGas ( m_data , 0 ) ;
htmlInfo + = " <h4>Hex</h4> " + QString ( Div ( Mono ) ) + QString : : fromStdString ( toHex ( m_data ) ) + " </div> " ;
if ( ui - > gas - > value ( ) = = ui - > gas - > minimum ( ) )
{
ui - > gas - > setMinimum ( gasNeeded ) ;
ui - > gas - > setValue ( gasNeeded ) ;
}
else
ui - > gas - > setMinimum ( gasNeeded ) ;
// Determine the minimum amount of gas we need to play...
qint64 baseGas = ( qint64 ) Interface : : txGas ( m_data , 0 ) ;
qint64 gasNeeded = 0 ;
if ( ! ui - > gas - > isEnabled ( ) )
ui - > gas - > setValue ( m_backupGas ) ;
ui - > gas - > setEnabled ( true ) ;
}
else
{
natspec + = " Destination not a contract. " ;
if ( ui - > gas - > isEnabled ( ) )
m_backupGas = ui - > gas - > value ( ) ;
ui - > gas - > setMinimum ( base ) ;
ui - > gas - > setValue ( base ) ;
ui - > gas - > setEnabled ( false ) ;
}
ui - > code - > setHtml ( errs + " <h3>NatSpec</h3> " + natspec + " <h3>Dump</h3> " + QString : : fromStdString ( dev : : memDump ( m_data , 8 , true ) ) + " <h3>Hex</h3> " + Div ( Mono ) + QString : : fromStdString ( toHex ( m_data ) ) + " </div> " ) ;
if ( b < value ( ) + baseGas * gasPrice ( ) )
{
// Not enough - bail.
bail ( " <div class= \" error \" ><span class= \" icon \" >ERROR</span> No single account contains enough for paying even the basic amount of gas required.</div> " ) ;
return ;
}
else
gasNeeded = min < qint64 > ( ( qint64 ) ethereum ( ) - > gasLimitRemaining ( ) , ( qint64 ) ( ( b - value ( ) ) / gasPrice ( ) ) ) ;
// Dry-run execution to determine gas requirement and any execution errors
Address to ;
ExecutionResult er ;
if ( isCreation ( ) )
er = ethereum ( ) - > create ( s , value ( ) , m_data , gasNeeded , gasPrice ( ) ) ;
else
{
to = m_context - > fromString ( ui - > destination - > currentText ( ) ) ;
er = ethereum ( ) - > call ( s , value ( ) , to , m_data , ethereum ( ) - > gasLimitRemaining ( ) , gasPrice ( ) ) ;
}
gasNeeded = ( qint64 ) er . gasUsed ;
htmlInfo = QString ( " <div class= \" info \" ><span class= \" icon \" >INFO</span> Gas required: %1 total = %2 base, %3 exec</div> " ) . arg ( gasNeeded ) . arg ( baseGas ) . arg ( gasNeeded - baseGas ) + htmlInfo ;
if ( er . excepted ! = TransactionException : : None )
{
bail ( " <div class= \" error \" ><span class= \" icon \" >ERROR</span> " + QString : : fromStdString ( toString ( er . excepted ) ) + " </div> " ) ;
return ;
}
if ( er . codeDeposit = = CodeDeposit : : Failed )
{
bail ( " <div class= \" error \" ><span class= \" icon \" >ERROR</span> Code deposit failed due to insufficient gas</div> " ) ;
return ;
}
// Add Natspec information
if ( ! isCreation ( ) )
htmlInfo = " <div class= \" info \" ><span class= \" icon \" >INFO</span> " + QString : : fromStdString ( natspecNotice ( to , m_data ) ) . toHtmlEscaped ( ) + " </div> " + htmlInfo ;
// Update gas
if ( ui - > gas - > value ( ) = = ui - > gas - > minimum ( ) )
{
ui - > gas - > setMinimum ( gasNeeded ) ;
ui - > gas - > setValue ( gasNeeded ) ;
}
else
ui - > gas - > setMinimum ( gasNeeded ) ;
updateFee ( ) ;
ui - > code - > setHtml ( htmlInfo ) ;
ui - > send - > setEnabled ( m_allGood ) ;
}
Secret Transact : : findSecret ( u256 _totalReq ) const
{
if ( ethereum ( ) )
for ( auto const & i : m_myKeys )
if ( ethereum ( ) - > balanceAt ( i . address ( ) , PendingBlock ) > = _totalReq )
return i . secret ( ) ;
return Secret ( ) ;
if ( ! ethereum ( ) )
return Secret ( ) ;
Secret best ;
u256 bestBalance = 0 ;
for ( auto const & i : m_myKeys )
{
auto b = ethereum ( ) - > balanceAt ( i . address ( ) , PendingBlock ) ;
if ( b > = _totalReq )
return i . secret ( ) ;
if ( b > bestBalance )
bestBalance = b , best = i . secret ( ) ;
}
return best ;
}
void Transact : : on_send_clicked ( )
{
Secret s = findSecret ( value ( ) + fee ( ) ) ;
if ( ! s )
auto b = ethereum ( ) - > balanceAt ( KeyPair ( s ) . address ( ) , PendingBlock ) ;
if ( ! s | | b < value ( ) + fee ( ) )
{
QMessageBox : : critical ( this , " Transaction Failed " , " Couldn't make transaction: no single account contains at least the required amount. " ) ;
return ;
@ -400,9 +406,7 @@ void Transact::on_send_clicked()
m_natSpecDB - > add ( contractHash , compiler . getMetadata ( s , dev : : solidity : : DocumentationType : : NatspecUser ) ) ;
}
}
catch ( . . . )
{
}
catch ( . . . ) { }
}
else
ethereum ( ) - > submitTransaction ( s , value ( ) , m_context - > fromString ( ui - > destination - > currentText ( ) ) , m_data , ui - > gas - > value ( ) , gasPrice ( ) ) ;
@ -411,24 +415,24 @@ void Transact::on_send_clicked()
void Transact : : on_debug_clicked ( )
{
Secret s = findSecret ( value ( ) + fee ( ) ) ;
auto b = ethereum ( ) - > balanceAt ( KeyPair ( s ) . address ( ) , PendingBlock ) ;
if ( ! s | | b < value ( ) + fee ( ) )
{
QMessageBox : : critical ( this , " Transaction Failed " , " Couldn't make transaction: no single account contains at least the required amount. " ) ;
return ;
}
try
{
u256 totalReq = value ( ) + fee ( ) ;
for ( auto i : m_myKeys )
if ( ethereum ( ) - > balanceAt ( i . address ( ) ) > = totalReq )
{
State st ( ethereum ( ) - > postState ( ) ) ;
Secret s = i . secret ( ) ;
Transaction t = isCreation ( ) ?
Transaction ( value ( ) , gasPrice ( ) , ui - > gas - > value ( ) , m_data , st . transactionsFrom ( dev : : toAddress ( s ) ) , s ) :
Transaction ( value ( ) , gasPrice ( ) , ui - > gas - > value ( ) , m_context - > fromString ( ui - > destination - > currentText ( ) ) , m_data , st . transactionsFrom ( dev : : toAddress ( s ) ) , s ) ;
Debugger dw ( m_context , this ) ;
Executive e ( st , ethereum ( ) - > blockChain ( ) , 0 ) ;
dw . populate ( e , t ) ;
dw . exec ( ) ;
return ;
}
QMessageBox : : critical ( this , " Transaction Failed " , " Couldn't make transaction: no single account contains at least the required amount. " ) ;
State st ( ethereum ( ) - > postState ( ) ) ;
Transaction t = isCreation ( ) ?
Transaction ( value ( ) , gasPrice ( ) , ui - > gas - > value ( ) , m_data , st . transactionsFrom ( dev : : toAddress ( s ) ) , s ) :
Transaction ( value ( ) , gasPrice ( ) , ui - > gas - > value ( ) , m_context - > fromString ( ui - > destination - > currentText ( ) ) , m_data , st . transactionsFrom ( dev : : toAddress ( s ) ) , s ) ;
Debugger dw ( m_context , this ) ;
Executive e ( st , ethereum ( ) - > blockChain ( ) , 0 ) ;
dw . populate ( e , t ) ;
dw . exec ( ) ;
}
catch ( dev : : Exception const & _e )
{