p;
if (!isCreation())
{
if (!ui->destination->currentData().isNull() && ui->destination->currentText() == ui->destination->itemText(ui->destination->currentIndex()))
p.first = Address(ui->destination->currentData().toString().trimmed().toStdString());
else
p = m_main->fromString(ui->destination->currentText().trimmed().toStdString());
}
return p;
}
void Transact::timerEvent(QTimerEvent*)
{
Address from = fromAccount();
Address to = toAccount().first;
if (m_upperBound != m_lowerBound)
{
qint64 mid = (m_lowerBound + m_upperBound) / 2;
ExecutionResult er;
if (isCreation())
er = ethereum()->create(from, value(), m_data, mid, gasPrice(), PendingBlock, FudgeFactor::Lenient);
else
er = ethereum()->call(from, value(), to, m_data, mid, gasPrice(), PendingBlock, FudgeFactor::Lenient);
if (er.excepted == TransactionException::OutOfGas || er.excepted == TransactionException::OutOfGasBase || er.excepted == TransactionException::OutOfGasIntrinsic || er.codeDeposit == CodeDeposit::Failed)
m_lowerBound = m_lowerBound == mid ? m_upperBound : mid;
else
{
m_lastGood = er;
m_upperBound = m_upperBound == mid ? m_lowerBound : mid;
}
updateBounds();
}
finaliseBounds();
}
void Transact::updateBounds()
{
ui->minGas->setValue(m_lowerBound);
ui->maxGas->setValue(m_upperBound);
double oran = m_startUpperBound - m_startLowerBound;
double nran = m_upperBound - m_lowerBound;
int x = int(log2(oran / nran) * 100.0 / log2(oran * 2));
ui->progressGas->setValue(x);
ui->progressGas->setVisible(true);
ui->gas->setSpecialValueText(QString("Auto (%1 gas)").arg(m_upperBound));
}
void Transact::finaliseBounds()
{
killTimer(m_gasCalcTimer);
quint64 baseGas = (quint64)Transaction::gasRequired(m_data, 0);
ui->progressGas->setVisible(false);
quint64 executionGas = m_upperBound - baseGas;
QString htmlInfo = QString("INFO Gas required: %1 total = %2 base, %3 exec [%4 refunded later]
").arg(m_upperBound).arg(baseGas).arg(executionGas).arg((qint64)m_lastGood.gasRefunded);
auto bail = [&](QString he) {
ui->send->setEnabled(false);
ui->code->setHtml(he + htmlInfo + m_dataInfo);
};
auto s = fromAccount();
auto b = ethereum()->balanceAt(s, PendingBlock);
if (b < value() + baseGas * gasPrice())
{
// Not enough - bail.
bail("ERROR Account doesn't contain enough for paying even the basic amount of gas required.
");
return;
}
if (m_upperBound > m_ethereum->gasLimitRemaining())
{
// Not enough - bail.
bail("ERROR Gas remaining in block isn't enough to allow the gas required.
");
return;
}
if (m_lastGood.excepted != TransactionException::None)
{
bail("ERROR " + QString::fromStdString(toString(m_lastGood.excepted)) + "
");
return;
}
if (m_lastGood.codeDeposit == CodeDeposit::Failed)
{
bail("ERROR Code deposit failed due to insufficient gas; " + QString::fromStdString(toString(m_lastGood.gasForDeposit)) + " GAS < " + QString::fromStdString(toString(m_lastGood.depositSize)) + " bytes * " + QString::fromStdString(toString(c_createDataGas)) + "GAS/byte
");
return;
}
updateFee();
ui->code->setHtml(htmlInfo + m_dataInfo);
ui->send->setEnabled(true);
}
GasRequirements Transact::determineGasRequirements()
{
// Determine the minimum amount of gas we need to play...
qint64 baseGas = (qint64)Transaction::gasRequired(m_data, 0);
Address from = fromAccount();
Address to = toAccount().first;
ExecutionResult lastGood;
m_startLowerBound = baseGas;
m_startUpperBound = (qint64)ethereum()->gasLimitRemaining();
for (unsigned i = 0; i < 30; ++i)
{
qint64 mid = m_startUpperBound;
ExecutionResult er;
if (isCreation())
er = ethereum()->create(from, value(), m_data, mid, gasPrice(), PendingBlock, FudgeFactor::Lenient);
else
er = ethereum()->call(from, value(), to, m_data, mid, gasPrice(), PendingBlock, FudgeFactor::Lenient);
if (er.excepted == TransactionException::OutOfGas || er.excepted == TransactionException::OutOfGasBase || er.excepted == TransactionException::OutOfGasIntrinsic || er.codeDeposit == CodeDeposit::Failed)
{
m_startLowerBound = mid;
m_startUpperBound *= 2;
}
else
{
// Begin async binary chop for gas calculation..
m_lastGood = lastGood;
m_lowerBound = m_startLowerBound;
m_upperBound = m_startUpperBound;
killTimer(m_gasCalcTimer);
m_gasCalcTimer = startTimer(0);
return GasRequirements{m_upperBound, baseGas, m_upperBound - baseGas, (qint64)lastGood.gasRefunded, lastGood};
}
}
return GasRequirements();
}
void Transact::rejigData()
{
if (!ethereum())
return;
// Determine how much balance we have to play with...
//findSecret(value() + ethereum()->gasLimitRemaining() * gasPrice());
auto s = fromAccount();
if (!s)
return;
QString htmlInfo;
auto bail = [&](QString he) {
ui->send->setEnabled(false);
m_dataInfo = he + htmlInfo;
ui->code->setHtml(m_dataInfo);
};
// 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(ETH_HTML_DIV(ETH_HTML_MONO)) + QString::fromStdString(toHex(m_data)) + "";
// Add Natspec information
if (!isCreation())
htmlInfo = "INFO " + QString::fromStdString(natspecNotice(toAccount().first, m_data)).toHtmlEscaped() + "
" + htmlInfo;
determineGasRequirements();
m_dataInfo = htmlInfo;
ui->code->setHtml(m_dataInfo);
ui->send->setEnabled(true);
}
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_main->retrieveSecret(best);
}
Address Transact::fromAccount()
{
if (ui->from->currentIndex() < 0 || ui->from->currentIndex() >= (int)m_accounts.size())
return Address();
auto it = m_accounts.begin();
std::advance(it, ui->from->currentIndex());
return *it;
}
void Transact::updateNonce()
{
u256 n = ethereum()->countAt(fromAccount(), PendingBlock);
ui->nonce->setMaximum((unsigned)n);
ui->nonce->setMinimum(0);
ui->nonce->setValue((unsigned)n);
}
void Transact::on_send_clicked()
{
// Secret s = findSecret(value() + fee());
u256 nonce = ui->autoNonce->isChecked() ? ethereum()->countAt(fromAccount(), PendingBlock) : ui->nonce->value();
auto a = fromAccount();
auto b = ethereum()->balanceAt(a, PendingBlock);
if (!a || b < value() + fee())
{
QMessageBox::critical(nullptr, "Transaction Failed", "Couldn't make transaction: account doesn't contain at least the required amount.", QMessageBox::Ok);
return;
}
Secret s = m_main->retrieveSecret(a);
if (!s)
return;
if (isCreation())
{
// If execution is a contract creation, add Natspec to
// a local Natspec LEVELDB
ethereum()->submitTransaction(s, value(), m_data, gas(), gasPrice(), nonce);
#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(), toAccount().first, m_data, gas(), gasPrice(), nonce);
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: account doesn't contain at least the required amount.");
return;
}
try
{
Block postState(ethereum()->postState());
Transaction t = isCreation() ?
Transaction(value(), gasPrice(), gas(), m_data, postState.transactionsFrom(from)) :
Transaction(value(), gasPrice(), gas(), toAccount().first, m_data, postState.transactionsFrom(from));
t.forceSender(from);
Debugger dw(m_main, this);
Executive e(postState, 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.
}
}