From c49c7d6be122590207ec9a32e346930ecea2c112 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 8 Aug 2015 00:21:50 +0200 Subject: [PATCH 1/2] First efforts at accurate gas. --- alethzero/Transact.cpp | 88 +++++++++++++++++++++++++------------- alethzero/Transact.h | 11 +++++ libethereum/ClientBase.cpp | 2 + libethereum/Executive.cpp | 2 +- 4 files changed, 73 insertions(+), 30 deletions(-) diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 34a37c241..899118e1c 100644 --- a/alethzero/Transact.cpp +++ b/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,38 @@ void Transact::rejigData() htmlInfo += "

Hex

" + QString(ETH_HTML_DIV(ETH_HTML_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; + auto gasReq = determineGasRequirements(); + htmlInfo = QString("
INFO Gas required: %1 total = %2 base, %3 exec [%4 refunded later]
").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("
ERROR Account doesn't contain enough for paying even the basic amount of gas required.
"); return; } - else - gasNeeded = (qint64)min(ethereum()->gasLimitRemaining(), ((b - value()) / max(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 - { - // 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 + c_callStipend); - 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) + if (gasReq.er.excepted != TransactionException::None) { - bail("
ERROR " + QString::fromStdString(toString(er.excepted)) + "
"); + bail("
ERROR " + QString::fromStdString(toString(gasReq.er.excepted)) + "
"); return; } - if (er.codeDeposit == CodeDeposit::Failed) + if (gasReq.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
"); + bail("
ERROR Code deposit failed due to insufficient gas; " + QString::fromStdString(toString(gasReq.er.gasForDeposit)) + " GAS < " + QString::fromStdString(toString(gasReq.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; + htmlInfo = "
INFO " + QString::fromStdString(natspecNotice(toAccount(), m_data)).toHtmlEscaped() + "
" + 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(); diff --git a/alethzero/Transact.h b/alethzero/Transact.h index a1ce544f9..9b2c77a6f 100644 --- a/alethzero/Transact.h +++ b/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; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 71c359093..43697b43e 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -85,6 +85,8 @@ ExecutionResult ClientBase::call(Address const& _from, u256 _value, Address _des ExecutionResult ClientBase::create(Address const& _from, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff) { + cdebug << "*** HERE!!!"; + cnote << "*** HERE!!!"; ExecutionResult ret; try { diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 7eb37ff99..839d69216 100644 --- a/libethereum/Executive.cpp +++ b/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(); From 10dce0517e22e1d20d5d17d738cf2309329c076f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 8 Aug 2015 00:52:02 +0200 Subject: [PATCH 2/2] Fixes for determination of gas. --- alethzero/Transact.cpp | 6 ++++++ libethereum/BlockChain.cpp | 2 +- libethereum/ClientBase.cpp | 4 +--- libethereum/State.cpp | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 899118e1c..998f1e160 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -427,6 +427,12 @@ void Transact::rejigData() bail("
ERROR Account doesn't contain enough for paying even the basic amount of gas required.
"); return; } + if (gasReq.neededGas > m_ethereum->gasLimitRemaining()) + { + // Not enough - bail. + bail("
ERROR Gas remaining in block isn't enough to allow the gas required.
"); + return; + } if (gasReq.er.excepted != TransactionException::None) { bail("
ERROR " + QString::fromStdString(toString(gasReq.er.excepted)) + "
"); diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 546f29b3e..14d199369 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -394,7 +394,7 @@ tuple 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()); diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 43697b43e..60ac3f32d 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -85,8 +85,6 @@ ExecutionResult ClientBase::call(Address const& _from, u256 _value, Address _des ExecutionResult ClientBase::create(Address const& _from, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff) { - cdebug << "*** HERE!!!"; - cnote << "*** HERE!!!"; ExecutionResult ret; try { @@ -96,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 (...) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index b7367a64f..bd5feacb0 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -485,7 +485,7 @@ std::pair State::execute(EnvInfo const& _en else { commit(); - + #if ETH_PARANOIA && !ETH_FATDB ctrace << "Executed; now" << rootHash(); ctrace << old.diff(*this);