123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- /*
- This file is part of cpp-ethereum.
- cpp-ethereum is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- cpp-ethereum is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
- */
- /** @file Executive.cpp
- * @author Gav Wood <i@gavwood.com>
- * @date 2014
- */
- #include "Executive.h"
- #include <boost/timer.hpp>
- #include <json/json.h>
- #include <libdevcore/CommonIO.h>
- #include <libevm/VMFactory.h>
- #include <libevm/VM.h>
- #include <libethcore/CommonJS.h>
- #include "Interface.h"
- #include "State.h"
- #include "ExtVM.h"
- #include "BlockChain.h"
- #include "Block.h"
- using namespace std;
- using namespace dev;
- using namespace dev::eth;
- const char* VMTraceChannel::name() { return "EVM"; }
- const char* ExecutiveWarnChannel::name() { return WarnChannel::name(); }
- StandardTrace::StandardTrace():
- m_trace(Json::arrayValue)
- {}
- bool changesMemory(Instruction _inst)
- {
- return
- _inst == Instruction::MSTORE ||
- _inst == Instruction::MSTORE8 ||
- _inst == Instruction::MLOAD ||
- _inst == Instruction::CREATE ||
- _inst == Instruction::CALL ||
- _inst == Instruction::CALLCODE ||
- _inst == Instruction::SHA3 ||
- _inst == Instruction::CALLDATACOPY ||
- _inst == Instruction::CODECOPY ||
- _inst == Instruction::EXTCODECOPY ||
- _inst == Instruction::DELEGATECALL;
- }
- bool changesStorage(Instruction _inst)
- {
- return _inst == Instruction::SSTORE;
- }
- void StandardTrace::operator()(uint64_t _steps, uint64_t PC, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
- {
- (void)_steps;
- ExtVM const& ext = dynamic_cast<ExtVM const&>(*voidExt);
- VM& vm = *voidVM;
- Json::Value r(Json::objectValue);
- Json::Value stack(Json::arrayValue);
- if (!m_options.disableStack)
- {
- for (auto const& i: vm.stack())
- stack.append("0x" + toHex(toCompactBigEndian(i, 1)));
- r["stack"] = stack;
- }
- bool newContext = false;
- Instruction lastInst = Instruction::STOP;
- if (m_lastInst.size() == ext.depth)
- {
- // starting a new context
- assert(m_lastInst.size() == ext.depth);
- m_lastInst.push_back(inst);
- newContext = true;
- }
- else if (m_lastInst.size() == ext.depth + 2)
- {
- m_lastInst.pop_back();
- lastInst = m_lastInst.back();
- }
- else if (m_lastInst.size() == ext.depth + 1)
- {
- // continuing in previous context
- lastInst = m_lastInst.back();
- m_lastInst.back() = inst;
- }
- else
- {
- cwarn << "GAA!!! Tracing VM and more than one new/deleted stack frame between steps!";
- cwarn << "Attmepting naive recovery...";
- m_lastInst.resize(ext.depth + 1);
- }
- Json::Value memJson(Json::arrayValue);
- if (!m_options.disableMemory && (changesMemory(lastInst) || newContext))
- {
- for (unsigned i = 0; i < vm.memory().size(); i += 32)
- {
- bytesConstRef memRef(vm.memory().data() + i, 32);
- memJson.append(toHex(memRef, 2, HexPrefix::DontAdd));
- }
- r["memory"] = memJson;
- }
- if (!m_options.disableStorage && (m_options.fullStorage || changesStorage(lastInst) || newContext))
- {
- Json::Value storage(Json::objectValue);
- for (auto const& i: ext.state().storage(ext.myAddress))
- storage["0x" + toHex(toCompactBigEndian(i.first, 1))] = "0x" + toHex(toCompactBigEndian(i.second, 1));
- r["storage"] = storage;
- }
- if (m_showMnemonics)
- r["op"] = instructionInfo(inst).name;
- r["pc"] = toString(PC);
- r["gas"] = toString(gas);
- r["gasCost"] = toString(gasCost);
- if (!!newMemSize)
- r["memexpand"] = toString(newMemSize);
- m_trace.append(r);
- }
- string StandardTrace::json(bool _styled) const
- {
- if (!_styled)
- {
- Json::StreamWriterBuilder wbuilder;
- wbuilder["indentation"] = "";
- return Json::writeString(wbuilder, m_trace);
- }
- else
- {
- Json::StreamWriterBuilder wbuilder;
- wbuilder["indentation"] = "";
- wbuilder["commentStyle"] = "All";
- return Json::writeString(wbuilder, m_trace);
- }
- // return _styled ? Json::StyledWriter().write(m_trace) : Json::FastWriter().write(m_trace);
- }
- Executive::Executive(Block& _s, BlockChain const& _bc, unsigned _level):
- m_s(_s.mutableState()),
- m_envInfo(_s.info(), _bc.lastHashes(_s.info().parentHash())),
- m_depth(_level),
- m_sealEngine(*_bc.sealEngine())
- {
- }
- Executive::Executive(Block& _s, LastHashes const& _lh, unsigned _level):
- m_s(_s.mutableState()),
- m_envInfo(_s.info(), _lh),
- m_depth(_level),
- m_sealEngine(*_s.sealEngine())
- {
- }
- Executive::Executive(State& _s, Block const& _block, unsigned _txIndex, BlockChain const& _bc, unsigned _level):
- m_s(_s = _block.fromPending(_txIndex)),
- m_envInfo(_block.info(), _bc.lastHashes(_block.info().parentHash()), _txIndex ? _block.receipt(_txIndex - 1).gasUsed() : 0),
- m_depth(_level),
- m_sealEngine(*_bc.sealEngine())
- {
- }
- u256 Executive::gasUsed() const
- {
- return m_t.gas() - m_gas;
- }
- u256 Executive::gasUsedNoRefunds() const
- {
- return m_t.gas() - m_gas + m_refunded;
- }
- void Executive::accrueSubState(SubState& _parentContext)
- {
- if (m_ext)
- _parentContext += m_ext->sub;
- }
- void Executive::initialize(Transaction const& _transaction)
- {
- m_t = _transaction;
- // Avoid transactions that would take us beyond the block gas limit.
- u256 startGasUsed = m_envInfo.gasUsed();
- if (startGasUsed + (bigint)m_t.gas() > m_envInfo.gasLimit())
- {
- clog(ExecutiveWarnChannel) << "Too much gas used in this block: Require <" << (m_envInfo.gasLimit() - startGasUsed) << " Got" << m_t.gas();
- m_excepted = TransactionException::BlockGasLimitReached;
- BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_envInfo.gasLimit() - startGasUsed), (bigint)m_t.gas()));
- }
- // Check gas cost is enough.
- m_baseGasRequired = m_t.gasRequired(m_sealEngine.evmSchedule(m_envInfo));
- if (m_baseGasRequired > m_t.gas())
- {
- clog(ExecutiveWarnChannel) << "Not enough gas to pay for the transaction: Require >" << m_baseGasRequired << " Got" << m_t.gas();
- m_excepted = TransactionException::OutOfGasBase;
- BOOST_THROW_EXCEPTION(OutOfGasBase() << RequirementError(m_baseGasRequired, (bigint)m_t.gas()));
- }
- // Avoid invalid transactions.
- u256 nonceReq;
- try
- {
- nonceReq = m_s.getNonce(m_t.sender());
- }
- catch (...)
- {
- clog(ExecutiveWarnChannel) << "Invalid Signature";
- m_excepted = TransactionException::InvalidSignature;
- throw;
- }
- if (m_t.nonce() != nonceReq)
- {
- clog(ExecutiveWarnChannel) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce();
- m_excepted = TransactionException::InvalidNonce;
- BOOST_THROW_EXCEPTION(InvalidNonce() << RequirementError((bigint)nonceReq, (bigint)m_t.nonce()));
- }
- // Avoid unaffordable transactions.
- m_gasCost = (bigint)m_t.gas() * m_t.gasPrice();
- bigint totalCost = m_t.value() + m_gasCost;
- if (m_s.balance(m_t.sender()) < totalCost)
- {
- clog(ExecutiveWarnChannel) << "Not enough cash: Require >" << totalCost << "=" << m_t.gas() << "*" << m_t.gasPrice() << "+" << m_t.value() << " Got" << m_s.balance(m_t.sender()) << "for sender: " << m_t.sender();
- m_excepted = TransactionException::NotEnoughCash;
- BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(totalCost, (bigint)m_s.balance(m_t.sender())) << errinfo_comment(m_t.sender().abridged()));
- }
- }
- bool Executive::execute()
- {
- // Entry point for a user-executed transaction.
- // Pay...
- clog(StateDetail) << "Paying" << formatBalance(u256(m_gasCost)) << "from sender for gas (" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")";
- m_s.subBalance(m_t.sender(), m_gasCost);
- if (m_t.isCreation())
- return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_baseGasRequired, &m_t.data(), m_t.sender());
- else
- return call(m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_baseGasRequired);
- }
- bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas)
- {
- CallParameters params{_senderAddress, _receiveAddress, _receiveAddress, _value, _value, _gas, _data, {}, {}};
- return call(params, _gasPrice, _senderAddress);
- }
- bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address const& _origin)
- {
- m_isCreation = false;
- // If external transaction.
- if (m_t)
- // Increment associated nonce for sender.
- m_s.incNonce(_p.senderAddress);
- if (m_sealEngine.isPrecompiled(_p.codeAddress))
- {
- bigint g = m_sealEngine.costOfPrecompiled(_p.codeAddress, _p.data);
- if (_p.gas < g)
- {
- m_excepted = TransactionException::OutOfGasBase;
- // Bail from exception.
- return true; // true actually means "all finished - nothing more to be done regarding go().
- }
- else
- {
- m_gas = (u256)(_p.gas - g);
- m_sealEngine.executePrecompiled(_p.codeAddress, _p.data, _p.out);
- }
- }
- else
- {
- m_gas = _p.gas;
- if (m_s.addressHasCode(_p.codeAddress))
- {
- m_outRef = _p.out; // Save ref to expected output buffer to be used in go()
- bytes const& c = m_s.code(_p.codeAddress);
- h256 codeHash = m_s.codeHash(_p.codeAddress);
- m_ext = make_shared<ExtVM>(m_s, m_envInfo, m_sealEngine, _p.receiveAddress, _p.senderAddress, _origin, _p.apparentValue, _gasPrice, _p.data, &c, codeHash, m_depth);
- }
- }
- m_s.transferBalance(_p.senderAddress, _p.receiveAddress, _p.valueTransfer);
- return !m_ext;
- }
- bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _init, Address _origin)
- {
- m_isCreation = true;
- u256 nonce = m_s.getNonce(_sender);
- m_s.incNonce(_sender);
- // We can allow for the reverted state (i.e. that with which m_ext is constructed) to contain the m_newAddress, since
- // we delete it explicitly if we decide we need to revert.
- m_newAddress = right160(sha3(rlpList(_sender, nonce)));
- m_gas = _gas;
- // Execute _init.
- if (!_init.empty())
- m_ext = make_shared<ExtVM>(m_s, m_envInfo, m_sealEngine, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, sha3(_init), m_depth);
- bool incrementNonce = m_envInfo.number() >= m_sealEngine.chainParams().u256Param("EIP158ForkBlock");
- m_s.createContract(m_newAddress, incrementNonce);
- m_s.transferBalance(_sender, m_newAddress, _endowment);
- if (_init.empty())
- m_s.setCode(m_newAddress, {});
- return !m_ext;
- }
- OnOpFunc Executive::simpleTrace()
- {
- return [](uint64_t steps, uint64_t PC, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
- {
- ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
- VM& vm = *voidVM;
- ostringstream o;
- o << endl << " STACK" << endl;
- for (auto i: vm.stack())
- o << (h256)i << endl;
- o << " MEMORY" << endl << ((vm.memory().size() > 1000) ? " mem size greater than 1000 bytes " : memDump(vm.memory()));
- o << " STORAGE" << endl;
- for (auto const& i: ext.state().storage(ext.myAddress))
- o << showbase << hex << i.first << ": " << i.second << endl;
- dev::LogOutputStream<VMTraceChannel, false>() << o.str();
- dev::LogOutputStream<VMTraceChannel, false>() << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << PC << " : " << instructionInfo(inst).name << " : " << dec << gas << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
- };
- }
- bool Executive::go(OnOpFunc const& _onOp)
- {
- if (m_ext)
- {
- #if ETH_TIMED_EXECUTIONS
- Timer t;
- #endif
- try
- {
- // Create VM instance. Force Interpreter if tracing requested.
- auto vm = _onOp ? VMFactory::create(VMKind::Interpreter) : VMFactory::create();
- if (m_isCreation)
- {
- auto out = vm->exec(m_gas, *m_ext, _onOp);
- if (m_res)
- {
- m_res->gasForDeposit = m_gas;
- m_res->depositSize = out.size();
- }
- if (out.size() > m_ext->evmSchedule().maxCodeSize)
- BOOST_THROW_EXCEPTION(OutOfGas());
- else if (out.size() * m_ext->evmSchedule().createDataGas <= m_gas)
- {
- if (m_res)
- m_res->codeDeposit = CodeDeposit::Success;
- m_gas -= out.size() * m_ext->evmSchedule().createDataGas;
- }
- else
- {
- if (m_ext->evmSchedule().exceptionalFailedCodeDeposit)
- BOOST_THROW_EXCEPTION(OutOfGas());
- else
- {
- if (m_res)
- m_res->codeDeposit = CodeDeposit::Failed;
- out.clear();
- }
- }
- if (m_res)
- m_res->output = out; // copy output to execution result
- m_s.setCode(m_newAddress, std::move(out));
- }
- else
- {
- if (m_res)
- {
- m_res->output = vm->exec(m_gas, *m_ext, _onOp); // take full output
- bytesConstRef{&m_res->output}.copyTo(m_outRef);
- }
- else
- vm->exec(m_gas, *m_ext, m_outRef, _onOp); // take only expected output
- }
- }
- catch (VMException const& _e)
- {
- clog(StateSafeExceptions) << "Safe VM Exception. " << diagnostic_information(_e);
- m_gas = 0;
- m_excepted = toTransactionException(_e);
- m_ext->revert();
- if (m_isCreation)
- m_newAddress = Address();
- }
- catch (Exception const& _e)
- {
- // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
- cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e);
- exit(1);
- // Another solution would be to reject this transaction, but that also
- // has drawbacks. Essentially, the amount of ram has to be increased here.
- }
- catch (std::exception const& _e)
- {
- // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
- cwarn << "Unexpected std::exception in VM. Not enough RAM? " << _e.what();
- exit(1);
- // Another solution would be to reject this transaction, but that also
- // has drawbacks. Essentially, the amount of ram has to be increased here.
- }
- #if ETH_TIMED_EXECUTIONS
- cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas);
- #endif
- }
- return true;
- }
- void Executive::finalize()
- {
- // Accumulate refunds for suicides.
- if (m_ext)
- m_ext->sub.refunds += m_ext->evmSchedule().suicideRefundGas * m_ext->sub.suicides.size();
- // SSTORE refunds...
- // must be done before the miner gets the fees.
- m_refunded = m_ext ? min((m_t.gas() - m_gas) / 2, m_ext->sub.refunds) : 0;
- m_gas += m_refunded;
- if (m_t)
- {
- m_s.addBalance(m_t.sender(), m_gas * m_t.gasPrice());
- u256 feesEarned = (m_t.gas() - m_gas) * m_t.gasPrice();
- m_s.addBalance(m_envInfo.author(), feesEarned);
- }
- // Suicides...
- if (m_ext)
- for (auto a: m_ext->sub.suicides)
- m_s.kill(a);
- // Logs..
- if (m_ext)
- m_logs = m_ext->sub.logs;
- // cwarn << "m_gas" << m_gas << "m_refunded" << m_refunded << "gasUsed()" << gasUsed();
- if (m_res) // Collect results
- {
- m_res->gasUsed = gasUsed();
- m_res->excepted = m_excepted; // TODO: m_except is used only in ExtVM::call
- m_res->newAddress = m_newAddress;
- m_res->gasRefunded = m_ext ? m_ext->sub.refunds : 0;
- }
- }
|