Browse Source

Merge pull request #2759 from ethereum/accurategas

Accurate gas estimation in Transact dialog.
cl-refactor
Gav Wood 10 years ago
parent
commit
e8471c08ad
  1. 90
      alethzero/Transact.cpp
  2. 11
      alethzero/Transact.h
  3. 2
      libethereum/BlockChain.cpp
  4. 2
      libethereum/ClientBase.cpp
  5. 2
      libethereum/Executive.cpp
  6. 2
      libethereum/State.cpp

90
alethzero/Transact.cpp

@ -323,6 +323,54 @@ string Transact::natspecNotice(Address _to, bytes const& _data)
return "Destination not a contract.";
}
Address Transact::toAccount()
{
return isCreation() ? Address() : m_context->fromString(ui->destination->currentText().toStdString()).first;
}
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();
ExecutionResult lastGood;
bool haveUpperBound = false;
qint64 lowerBound = baseGas;
qint64 upperBound = (qint64)ethereum()->gasLimitRemaining();
for (unsigned i = 0; i < 20 && ((haveUpperBound && upperBound - lowerBound > 100) || !haveUpperBound); ++i) // get to with 100.
{
qint64 mid = haveUpperBound ? (lowerBound + upperBound) / 2 : upperBound;
cdebug << "Trying" << mid;
ExecutionResult er;
if (isCreation())
er = ethereum()->create(from, value(), m_data, mid, gasPrice(), ethereum()->getDefault(), FudgeFactor::Lenient);
else
er = ethereum()->call(from, value(), to, m_data, mid, gasPrice(), ethereum()->getDefault(), FudgeFactor::Lenient);
cdebug << toString(er.excepted);
if (er.excepted == TransactionException::OutOfGas || er.excepted == TransactionException::OutOfGasBase || er.excepted == TransactionException::OutOfGasIntrinsic || er.codeDeposit == CodeDeposit::Failed)
{
lowerBound = mid;
if (!haveUpperBound)
upperBound *= 2;
}
else
{
lastGood = er;
if (haveUpperBound)
upperBound = mid;
else
haveUpperBound = true;
}
}
// Dry-run execution to determine gas requirement and any execution errors
// (qint64)(er.gasUsed + er.gasRefunded + c_callStipend);
return GasRequirements{upperBound, baseGas, upperBound - baseGas, (qint64)lastGood.gasRefunded, lastGood};
}
void Transact::rejigData()
{
if (!ethereum())
@ -370,56 +418,44 @@ void Transact::rejigData()
htmlInfo += "<h4>Hex</h4>" + QString(ETH_HTML_DIV(ETH_HTML_MONO)) + QString::fromStdString(toHex(m_data)) + "</div>";
// Determine the minimum amount of gas we need to play...
qint64 baseGas = (qint64)Transaction::gasRequired(m_data, 0);
qint64 gasNeeded = 0;
auto gasReq = determineGasRequirements();
htmlInfo = QString("<div class=\"info\"><span class=\"icon\">INFO</span> Gas required: %1 total = %2 base, %3 exec [%4 refunded later]</div>").arg(gasReq.neededGas).arg(gasReq.baseGas).arg(gasReq.executionGas).arg(gasReq.refundedGas) + htmlInfo;
if (b < value() + baseGas * gasPrice())
if (b < value() + gasReq.baseGas * gasPrice())
{
// Not enough - bail.
bail("<div class=\"error\"><span class=\"icon\">ERROR</span> Account doesn't contain enough for paying even the basic amount of gas required.</div>");
return;
}
else
gasNeeded = (qint64)min<bigint>(ethereum()->gasLimitRemaining(), ((b - value()) / max<u256>(gasPrice(), 1)));
// 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
if (gasReq.neededGas > m_ethereum->gasLimitRemaining())
{
// TODO: cache like m_data.
to = m_context->fromString(ui->destination->currentText().toStdString()).first;
er = ethereum()->call(s, value(), to, m_data, gasNeeded, gasPrice());
// Not enough - bail.
bail("<div class=\"error\"><span class=\"icon\">ERROR</span> Gas remaining in block isn't enough to allow the gas required.</div>");
return;
}
gasNeeded = (qint64)(er.gasUsed + er.gasRefunded + c_callStipend);
htmlInfo = QString("<div class=\"info\"><span class=\"icon\">INFO</span> Gas required: %1 total = %2 base, %3 exec [%4 refunded later]</div>").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas).arg((qint64)er.gasRefunded) + htmlInfo;
if (er.excepted != TransactionException::None)
if (gasReq.er.excepted != TransactionException::None)
{
bail("<div class=\"error\"><span class=\"icon\">ERROR</span> " + QString::fromStdString(toString(er.excepted)) + "</div>");
bail("<div class=\"error\"><span class=\"icon\">ERROR</span> " + QString::fromStdString(toString(gasReq.er.excepted)) + "</div>");
return;
}
if (er.codeDeposit == CodeDeposit::Failed)
if (gasReq.er.codeDeposit == CodeDeposit::Failed)
{
bail("<div class=\"error\"><span class=\"icon\">ERROR</span> Code deposit failed due to insufficient gas; " + QString::fromStdString(toString(er.gasForDeposit)) + " GAS &lt; " + QString::fromStdString(toString(er.depositSize)) + " bytes * " + QString::fromStdString(toString(c_createDataGas)) + "GAS/byte</div>");
bail("<div class=\"error\"><span class=\"icon\">ERROR</span> Code deposit failed due to insufficient gas; " + QString::fromStdString(toString(gasReq.er.gasForDeposit)) + " GAS &lt; " + QString::fromStdString(toString(gasReq.er.depositSize)) + " bytes * " + QString::fromStdString(toString(c_createDataGas)) + "GAS/byte</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;
htmlInfo = "<div class=\"info\"><span class=\"icon\">INFO</span> " + QString::fromStdString(natspecNotice(toAccount(), m_data)).toHtmlEscaped() + "</div>" + htmlInfo;
// Update gas
if (ui->gas->value() == ui->gas->minimum())
{
ui->gas->setMinimum(gasNeeded);
ui->gas->setValue(gasNeeded);
ui->gas->setMinimum(gasReq.neededGas);
ui->gas->setValue(gasReq.neededGas);
}
else
ui->gas->setMinimum(gasNeeded);
ui->gas->setMinimum(gasReq.neededGas);
updateFee();

11
alethzero/Transact.h

@ -33,6 +33,15 @@ namespace Ui { class Transact; }
namespace dev { namespace eth { class Client; } }
namespace dev { namespace solidity { class CompilerStack; } }
struct GasRequirements
{
qint64 neededGas;
qint64 baseGas;
qint64 executionGas;
qint64 refundedGas;
dev::eth::ExecutionResult er;
};
class Transact: public QDialog
{
Q_OBJECT
@ -65,6 +74,7 @@ private:
void updateNonce();
dev::Address fromAccount();
dev::Address toAccount();
void updateDestination();
void updateFee();
bool isCreation() const;
@ -73,6 +83,7 @@ private:
dev::u256 value() const;
dev::u256 gasPrice() const;
dev::Address to() const;
GasRequirements determineGasRequirements();
std::string natspecNotice(dev::Address _to, dev::bytes const& _data);
dev::Secret findSecret(dev::u256 _totalReq) const;

2
libethereum/BlockChain.cpp

@ -394,7 +394,7 @@ tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB c
// Nonce & uncle nonces already verified in verification thread at this point.
ImportRoute r;
DEV_TIMED_ABOVE("Block import " + toString(block.verified.info.number()), 500)
r = import(block.verified, _stateDB, ImportRequirements::Everything & ~ImportRequirements::ValidSeal & ~ImportRequirements::CheckUncles != 0);
r = import(block.verified, _stateDB, (ImportRequirements::Everything & ~ImportRequirements::ValidSeal & ~ImportRequirements::CheckUncles) != 0);
fresh += r.liveBlocks;
dead += r.deadBlocks;
goodTransactions.reserve(goodTransactions.size() + r.goodTranactions.size());

2
libethereum/ClientBase.cpp

@ -94,7 +94,7 @@ ExecutionResult ClientBase::create(Address const& _from, u256 _value, bytes cons
Transaction t(_value, _gasPrice, _gas, _data, n);
t.forceSender(_from);
if (_ff == FudgeFactor::Lenient)
temp.mutableState().addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value()));
temp.mutableState().addBalance(_from, (u256)(t.gas() * t.gasPrice() + t.value()));
ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted);
}
catch (...)

2
libethereum/Executive.cpp

@ -383,7 +383,7 @@ bool Executive::go(OnOpFunc const& _onOp)
}
catch (VMException const& _e)
{
clog(StateSafeExceptions) << "Safe VM Exception. " << diagnostic_information(_e);
cnote/*clog(StateSafeExceptions)*/ << "Safe VM Exception. " << diagnostic_information(_e);
m_gas = 0;
m_excepted = toTransactionException(_e);
m_ext->revert();

2
libethereum/State.cpp

@ -485,7 +485,7 @@ std::pair<ExecutionResult, TransactionReceipt> State::execute(EnvInfo const& _en
else
{
commit();
#if ETH_PARANOIA && !ETH_FATDB
ctrace << "Executed; now" << rootHash();
ctrace << old.diff(*this);

Loading…
Cancel
Save