You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
6.8 KiB
232 lines
6.8 KiB
/*
|
|
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 <libevm/VM.h>
|
|
#include "Executive.h"
|
|
#include "State.h"
|
|
#include "ExtVM.h"
|
|
using namespace std;
|
|
using namespace eth;
|
|
|
|
#define EVM_TRACE 1
|
|
|
|
Executive::~Executive()
|
|
{
|
|
// TODO: Make safe.
|
|
delete m_ext;
|
|
delete m_vm;
|
|
}
|
|
|
|
u256 Executive::gasUsed() const
|
|
{
|
|
return m_t.gas - m_endGas;
|
|
}
|
|
|
|
bool Executive::setup(bytesConstRef _rlp)
|
|
{
|
|
// Entry point for a user-executed transaction.
|
|
m_t = Transaction(_rlp);
|
|
|
|
m_sender = m_t.sender();
|
|
|
|
// Avoid invalid transactions.
|
|
auto nonceReq = m_s.transactionsFrom(m_sender);
|
|
if (m_t.nonce != nonceReq)
|
|
{
|
|
clog(StateChat) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce;
|
|
throw InvalidNonce(nonceReq, m_t.nonce);
|
|
}
|
|
|
|
// Don't like transactions whose gas price is too low. NOTE: this won't stay here forever - it's just until we get a proper gas price discovery protocol going.
|
|
if (m_t.gasPrice < m_s.m_currentBlock.minGasPrice)
|
|
{
|
|
clog(StateChat) << "Offered gas-price is too low: Require >" << m_s.m_currentBlock.minGasPrice << " Got" << m_t.gasPrice;
|
|
throw GasPriceTooLow();
|
|
}
|
|
|
|
// Check gas cost is enough.
|
|
u256 gasCost = m_t.data.size() * c_txDataGas + c_txGas;
|
|
|
|
if (m_t.gas < gasCost)
|
|
{
|
|
clog(StateChat) << "Not enough gas to pay for the transaction: Require >" << gasCost << " Got" << m_t.gas;
|
|
throw OutOfGas();
|
|
}
|
|
|
|
u256 cost = m_t.value + m_t.gas * m_t.gasPrice;
|
|
|
|
// Avoid unaffordable transactions.
|
|
if (m_s.balance(m_sender) < cost)
|
|
{
|
|
clog(StateChat) << "Not enough cash: Require >" << cost << " Got" << m_s.balance(m_sender);
|
|
throw NotEnoughCash();
|
|
}
|
|
|
|
u256 startGasUsed = m_s.gasUsed();
|
|
if (startGasUsed + m_t.gas > m_s.m_currentBlock.gasLimit)
|
|
{
|
|
clog(StateChat) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas;
|
|
throw BlockGasLimitReached();
|
|
}
|
|
|
|
// Increment associated nonce for sender.
|
|
m_s.noteSending(m_sender);
|
|
|
|
// Pay...
|
|
// cnote << "Paying" << formatBalance(cost) << "from sender (includes" << m_t.gas << "gas at" << formatBalance(m_t.gasPrice) << ")";
|
|
m_s.subBalance(m_sender, cost);
|
|
|
|
if (m_t.isCreation())
|
|
return create(m_sender, m_t.value, m_t.gasPrice, m_t.gas - gasCost, &m_t.data, m_sender);
|
|
else
|
|
return call(m_t.receiveAddress, m_sender, m_t.value, m_t.gasPrice, bytesConstRef(&m_t.data), m_t.gas - gasCost, m_sender);
|
|
}
|
|
|
|
bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)
|
|
{
|
|
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
|
|
m_s.addBalance(_receiveAddress, _value);
|
|
|
|
if (m_s.addressHasCode(_receiveAddress))
|
|
{
|
|
m_vm = new VM(_gas);
|
|
bytes const& c = m_s.code(_receiveAddress);
|
|
m_ext = new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
m_endGas = _gas;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _init, Address _origin)
|
|
{
|
|
m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1)));
|
|
while (m_s.addressInUse(m_newAddress))
|
|
m_newAddress = (u160)m_newAddress + 1;
|
|
|
|
// Set up new account...
|
|
m_s.m_cache[m_newAddress] = AddressState(0, _endowment, h256(), h256());
|
|
|
|
// Execute _init.
|
|
m_vm = new VM(_gas);
|
|
m_ext = new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init);
|
|
return _init.empty();
|
|
}
|
|
|
|
bool Executive::go(uint64_t _steps)
|
|
{
|
|
if (m_vm)
|
|
{
|
|
bool revert = false;
|
|
try
|
|
{
|
|
#if ETH_VMTRACE
|
|
if (_steps == (uint64_t)0 - 1)
|
|
for (uint64_t s = 0;; ++s)
|
|
{
|
|
ostringstream o;
|
|
o << endl << " STACK" << endl;
|
|
for (auto i: vm().stack())
|
|
o << (h256)i << endl;
|
|
o << " MEMORY" << endl << memDump(vm().memory());
|
|
o << " STORAGE" << endl;
|
|
for (auto const& i: state().storage(ext().myAddress))
|
|
o << showbase << hex << i.first << ": " << i.second << endl;
|
|
eth::LogOutputStream<VMTraceChannel, false>(true) << o.str();
|
|
eth::LogOutputStream<VMTraceChannel, false>(false) << dec << " | #" << s << " | " << hex << setw(4) << setfill('0') << vm().curPC() << " : " << c_instructionInfo.at((Instruction)ext().getCode(vm().curPC())).name << " | " << dec << vm().gas() << " ]";
|
|
if (s >= _steps)
|
|
break;
|
|
try
|
|
{
|
|
m_out = m_vm->go(*m_ext, 1);
|
|
break;
|
|
}
|
|
catch (StepsDone const&) {}
|
|
}
|
|
else
|
|
m_out = m_vm->go(*m_ext, _steps);
|
|
#else
|
|
m_out = m_vm->go(*m_ext, _steps);
|
|
#endif
|
|
m_endGas = m_vm->gas();
|
|
}
|
|
catch (StepsDone const&)
|
|
{
|
|
return false;
|
|
}
|
|
catch (OutOfGas const& /*_e*/)
|
|
{
|
|
clog(StateChat) << "Out of Gas! Reverting.";
|
|
revert = true;
|
|
}
|
|
catch (VMException const& _e)
|
|
{
|
|
clog(StateChat) << "VM Exception: " << _e.description();
|
|
}
|
|
catch (Exception const& _e)
|
|
{
|
|
clog(StateChat) << "Exception in VM: " << _e.description();
|
|
}
|
|
catch (std::exception const& _e)
|
|
{
|
|
clog(StateChat) << "std::exception in VM: " << _e.what();
|
|
}
|
|
|
|
// Write state out only in the case of a non-excepted transaction.
|
|
if (revert)
|
|
{
|
|
m_ext->revert();
|
|
if (m_newAddress)
|
|
{
|
|
m_s.m_cache.erase(m_newAddress);
|
|
m_newAddress = Address();
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
u256 Executive::gas() const
|
|
{
|
|
return m_vm ? m_vm->gas() : m_endGas;
|
|
}
|
|
|
|
void Executive::finalize()
|
|
{
|
|
if (m_t.isCreation() && m_newAddress && m_out.size())
|
|
// non-reverted creation - put code in place.
|
|
m_s.m_cache[m_newAddress].setCode(m_out);
|
|
|
|
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
|
|
m_s.addBalance(m_sender, m_endGas * m_t.gasPrice);
|
|
|
|
u256 gasSpentInEth = (m_t.gas - m_endGas) * m_t.gasPrice;
|
|
/* unsigned c_feesKept = 8;
|
|
u256 feesEarned = gasSpentInEth - (gasSpentInEth / c_feesKept);
|
|
cnote << "Transferring" << (100.0 - 100.0 / c_feesKept) << "% of" << formatBalance(gasSpent) << "=" << formatBalance(feesEarned) << "to miner (" << formatBalance(gasSpentInEth - feesEarned) << "is burnt).";
|
|
*/
|
|
u256 feesEarned = gasSpentInEth;
|
|
// cnote << "Transferring" << formatBalance(gasSpent) << "to miner.";
|
|
m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned);
|
|
}
|
|
|