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);
}
#if ETH_SOLIDITY || !ETH_TRUE
else if (sourceIsSolidity(_user))
{
dev::solidity::CompilerStack compiler(true);
try
{
// compiler.addSources(dev::solidity::StandardSources);
data = compiler.compile(_user, _opt);
solidity = "Solidity
";
solidity += "var " + compiler.defaultContractName() + " = web3.eth.contract(" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped().toStdString() + ");
";
solidity += "" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped().toStdString() + "
";
solidity += "" + QString::fromStdString(getFunctionHashes(compiler, "")).toHtmlEscaped().toStdString() + "
";
}
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");
}
}
#endif
#if ETH_SERPENT
else if (sourceIsSerpent(_user))
{
try
{
data = dev::asBytes(::compile(_user));
}
catch (string const& err)
{
errors.push_back("Serpent " + err);
}
}
#endif
else
{
data = compileLLL(_user, _opt, &errors);
if (errors.empty())
{
auto asmcode = compileLLLToAsm(_user, _opt);
lll = "LLL
" + QString::fromStdString(asmcode).toHtmlEscaped().toStdString() + "
";
}
}
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
{
NatspecExpressionEvaluator evaluator;
return evaluator.evalExpression(QString::fromStdString(userNotice)).toStdString();
}
}
else
return "Destination not a contract.";
}
void Transact::rejigData()
{
if (!ethereum())
return;
// Determine how much balance we have to play with...
//findSecret(value() + ethereum()->gasLimitRemaining() * gasPrice());
auto s = fromAccount();
auto b = ethereum()->balanceAt(s, PendingBlock);
m_allGood = true;
QString htmlInfo;
auto bail = [&](QString he) {
m_allGood = false;
ui->send->setEnabled(false);
ui->code->setHtml(he + htmlInfo);
};
// Determine m_info.
if (isCreation())
{
string info;
vector errors;
tie(errors, m_data, info) = userInputToCode(ui->data->toPlainText().toStdString(), ui->optimize->isChecked());
if (errors.size())
{
// Errors determining transaction data (i.e. init code). Bail.
QString htmlErrors;
for (auto const& i: errors)
htmlErrors.append("ERROR " + QString::fromStdString(i).toHtmlEscaped() + "
");
bail(htmlErrors);
return;
}
htmlInfo = QString::fromStdString(info) + "Code
" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped();
}
else
{
m_data = parseData(ui->data->toPlainText().toStdString());
htmlInfo = "Dump
" + QString::fromStdString(dev::memDump(m_data, 8, true));
}
htmlInfo += "Hex
" + QString(Div(Mono)) + QString::fromStdString(toHex(m_data)) + "";
// Determine the minimum amount of gas we need to play...
qint64 baseGas = (qint64)Transaction::gasRequired(m_data, 0);
qint64 gasNeeded = 0;
if (b < value() + baseGas * gasPrice())
{
// Not enough - bail.
bail("ERROR No single account contains enough for paying even the basic amount of gas required.
");
return;
}
else
gasNeeded = (qint64)min(ethereum()->gasLimitRemaining(), ((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
{
// TODO: cache like m_data.
to = m_context->fromString(ui->destination->currentText().toStdString()).first;
er = ethereum()->call(s, value(), to, m_data, gasNeeded, gasPrice());
}
gasNeeded = (qint64)(er.gasUsed + er.gasRefunded);
htmlInfo = QString("INFO Gas required: %1 total = %2 base, %3 exec [%4 refunded later]
").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas).arg((qint64)er.gasRefunded) + htmlInfo;
if (er.excepted != TransactionException::None)
{
bail("ERROR " + QString::fromStdString(toString(er.excepted)) + "
");
return;
}
if (er.codeDeposit == CodeDeposit::Failed)
{
bail("ERROR Code deposit failed due to insufficient gas; " + QString::fromStdString(toString(er.gasForDeposit)) + " GAS < " + QString::fromStdString(toString(er.depositSize)) + " bytes * " + QString::fromStdString(toString(c_createDataGas)) + "GAS/byte
");
return;
}
// Add Natspec information
if (!isCreation())
htmlInfo = "INFO " + QString::fromStdString(natspecNotice(to, m_data)).toHtmlEscaped() + "
" + 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())
return Secret();
Address best;
u256 bestBalance = 0;
for (auto const& i: m_accounts)
{
auto b = ethereum()->balanceAt(i, PendingBlock);
if (b >= _totalReq)
{
best = i;
break;
}
if (b > bestBalance)
bestBalance = b, best = i;
}
return m_context->retrieveSecret(best);
}
Address Transact::fromAccount()
{
auto it = m_accounts.begin();
std::advance(it, ui->from->currentIndex());
return *it;
}
void Transact::on_send_clicked()
{
// Secret s = findSecret(value() + fee());
Secret s = m_context->retrieveSecret(fromAccount());
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;
}
if (isCreation())
{
// If execution is a contract creation, add Natspec to
// a local Natspec LEVELDB
ethereum()->submitTransaction(s, value(), m_data, ui->gas->value(), gasPrice());
#if ETH_SOLIDITY
string src = ui->data->toPlainText().toStdString();
if (sourceIsSolidity(src))
try
{
dev::solidity::CompilerStack compiler(true);
m_data = compiler.compile(src, ui->optimize->isChecked());
for (string const& s: compiler.getContractNames())
{
h256 contractHash = compiler.getContractCodeHash(s);
m_natSpecDB->add(contractHash, compiler.getMetadata(s, dev::solidity::DocumentationType::NatspecUser));
}
}
catch (...) {}
#endif
}
else
// TODO: cache like m_data.
ethereum()->submitTransaction(s, value(), m_context->fromString(ui->destination->currentText().toStdString()).first, m_data, ui->gas->value(), gasPrice());
close();
}
void Transact::on_debug_clicked()
{
// Secret s = findSecret(value() + fee());
Address from = fromAccount();
auto b = ethereum()->balanceAt(from, PendingBlock);
if (!from || b < value() + fee())
{
QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount.");
return;
}
try
{
State st(ethereum()->postState());
Transaction t = isCreation() ?
Transaction(value(), gasPrice(), ui->gas->value(), m_data, st.transactionsFrom(from)) :
Transaction(value(), gasPrice(), ui->gas->value(), m_context->fromString(ui->destination->currentText().toStdString()).first, m_data, st.transactionsFrom(from));
t.forceSender(from);
Debugger dw(m_context, this);
Executive e(st, ethereum()->blockChain(), 0);
dw.populate(e, t);
dw.exec();
}
catch (dev::Exception const& _e)
{
QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction. Low-level error: " + QString::fromStdString(diagnostic_information(_e)));
// this output is aimed at developers, reconsider using _e.what for more user friendly output.
}
}