diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 34a37c241..998f1e160 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,44 @@ 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 + 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("
ERROR Gas remaining in block isn't enough to allow the gas required.
"); + return; } - 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/BlockChain.cpp b/libethereum/BlockChain.cpp index b38db9c8b..9921342d7 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); + 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()); @@ -1278,6 +1278,7 @@ Block BlockChain::genesisBlock(OverlayDB const& _db) dev::eth::commit(m_genesisState, ret.mutableState().m_state); // bit horrible. maybe consider a better way of constructing it? ret.mutableState().db().commit(); // have to use this db() since it's the one that has been altered with the above commit. ret.m_previousBlock = BlockInfo(m_genesisBlock); + ret.resetCurrent(); return ret; } diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 71c359093..60ac3f32d 100644 --- a/libethereum/ClientBase.cpp +++ b/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 (...) 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(); 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); diff --git a/libjsengine/JSV8RPC.cpp b/libjsengine/JSV8RPC.cpp index a24420d43..5b6578ed8 100644 --- a/libjsengine/JSV8RPC.cpp +++ b/libjsengine/JSV8RPC.cpp @@ -45,7 +45,18 @@ v8::Handle JSV8RPCSend(v8::Arguments const& _args) v8::Local vals[1] = {_args[0]->ToObject()}; v8::Local stringifiedArg = stringifyFunc->Call(stringifyFunc, 1, vals); v8::String::Utf8Value str(stringifiedArg); - that->onSend(*str); + try + { + that->onSend(*str); + } + catch (std::exception const& _e) + { + return v8::ThrowException(v8::String::New(_e.what())); + } + catch (...) + { + return v8::ThrowException(v8::String::New("Unknown C++ exception.")); + } v8::Local values[1] = {v8::String::New(that->lastResponse())}; return parseFunc->Call(parseFunc, 1, values); diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 4bd5a126c..b3b3d5bf3 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -742,9 +742,14 @@ void Host::keepAlivePeers() return; RecursiveGuard l(x_sessions); - for (auto p: m_sessions) - if (auto pp = p.second.lock()) - pp->ping(); + for (auto it = m_sessions.begin(); it != m_sessions.end();) + if (auto p = it->second.lock()) + { + p->ping(); + ++it; + } + else + it = m_sessions.erase(it); m_lastPing = chrono::steady_clock::now(); }