Browse Source

Repotted much of State.

cl-refactor
Gav Wood 11 years ago
parent
commit
1bbafddd1e
  1. 1
      alethzero/MainWin.cpp
  2. 1
      libethcore/Common.h
  3. 2
      libethereum/AddressState.h
  4. 187
      libethereum/Executive.cpp
  5. 70
      libethereum/Executive.h
  6. 22
      libethereum/ExtVM.cpp
  7. 86
      libethereum/ExtVM.h
  8. 163
      libethereum/State.cpp
  9. 98
      libethereum/State.h
  10. 1
      test/vm.cpp

1
alethzero/MainWin.cpp

@ -18,6 +18,7 @@
#include <libethereum/Instruction.h>
#include <libethereum/PeerServer.h>
#include <libethereum/VM.h>
#include <libethereum/ExtVM.h>
#include "BuildInfo.h"
#include "MainWin.h"
#include "ui_Main.h"

1
libethcore/Common.h

@ -78,6 +78,7 @@ using HexMap = std::map<bytes, std::string>;
// Null/Invalid values for convenience.
static const u256 Invalid256 = ~(u256)0;
static const bytes NullBytes;
static const std::map<u256, u256> EmptyMapU256U256;
/// Trivial UnitTest type that everyone can agree on, mainly to allow befriending for test classes & their code.
template <unsigned T> class UnitTest {};

2
libethereum/AddressState.h

@ -27,7 +27,7 @@
namespace eth
{
// TODO: Document.
// TODO: Document fully.
class AddressState
{

187
libethereum/Executive.cpp

@ -0,0 +1,187 @@
/*
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 "State.h"
#include "ExtVM.h"
#include "VM.h"
using namespace std;
using namespace eth;
Executive::~Executive()
{
// TODO: Make safe.
delete m_ext;
delete m_vm;
}
void Executive::setup(bytesConstRef _rlp)
{
// Entry point for a user-executed transaction.
m_t = Transaction(_rlp);
auto sender = m_t.sender();
// Avoid invalid transactions.
auto nonceReq = m_s.transactionsFrom(sender);
if (m_t.nonce != nonceReq)
{
clog(StateChat) << "Invalid 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 < 10 * szabo)
{
clog(StateChat) << "Offered gas-price is too low.";
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.";
throw OutOfGas();
}
m_startGas = m_t.gas;
u256 cost = m_t.value + m_t.gas * m_t.gasPrice;
// Avoid unaffordable transactions.
if (m_s.balance(sender) < cost)
{
clog(StateChat) << "Not enough cash.";
throw NotEnoughCash();
}
// Increment associated nonce for sender.
m_s.noteSending(sender);
// Pay...
// cnote << "Paying" << formatBalance(cost) << "from sender (includes" << m_t.gas << "gas at" << formatBalance(m_t.gasPrice) << ")";
m_s.subBalance(sender, cost);
if (m_t.isCreation())
create(sender, m_t.value, m_t.gasPrice, m_t.gas - gasCost, &m_t.data, sender);
else
call(m_t.receiveAddress, sender, m_t.value, m_t.gasPrice, bytesConstRef(&m_t.data), m_t.gas - gasCost, sender);
}
void 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);
m_ext = new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &m_s.code(_receiveAddress));
}
else
m_endGas = _gas;
}
void 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, 0, h256(), h256());
// Execute _init.
m_vm = new VM(_gas);
m_ext = new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, _init, _init);
}
bool Executive::go(uint64_t _steps)
{
if (m_vm)
{
bool revert = false;
try
{
m_out = m_vm->go(*m_ext, _steps);
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->gas();
}
void Executive::finalize()
{
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
m_s.addBalance(m_ext->origin, m_endGas * m_ext->gasPrice);
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);
u256 gasSpent = (m_startGas - m_endGas) * m_ext->gasPrice;
/* unsigned c_feesKept = 8;
u256 feesEarned = gasSpent - (gasSpent / c_feesKept);
cnote << "Transferring" << (100.0 - 100.0 / c_feesKept) << "% of" << formatBalance(gasSpent) << "=" << formatBalance(feesEarned) << "to miner (" << formatBalance(gasSpent - feesEarned) << "is burnt).";
*/
u256 feesEarned = gasSpent;
// cnote << "Transferring" << formatBalance(gasSpent) << "to miner.";
m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned);
}

70
libethereum/Executive.h

@ -0,0 +1,70 @@
/*
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.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include "CommonEth.h"
#include "Transaction.h"
namespace eth
{
class VM;
class ExtVM;
class State;
class Executive
{
public:
Executive(State& _s): m_s(_s) {}
~Executive();
void setup(bytesConstRef _transaction);
void create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress);
void call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress);
bool go(uint64_t _steps = (unsigned)-1);
void finalize();
Transaction const& t() const { return m_t; }
u256 gas() const;
bytesConstRef out() const { return m_out; }
h160 newAddress() const { return m_newAddress; }
VM const& vm() const { return *m_vm; }
State const& state() const { return m_s; }
ExtVM const& ext() const { return *m_ext; }
private:
State& m_s;
ExtVM* m_ext = nullptr; // TODO: make safe.
VM* m_vm = nullptr;
bytesConstRef m_out;
Address m_newAddress;
Transaction m_t;
u256 m_startGas;
u256 m_endGas;
};
}

22
libethereum/ExtVM.cpp

@ -0,0 +1,22 @@
/*
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 ExtVM.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "ExtVM.h"

86
libethereum/ExtVM.h

@ -0,0 +1,86 @@
/*
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 ExtVM.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <map>
#include "CommonEth.h"
#include "State.h"
#include "ExtVMFace.h"
namespace eth
{
// TODO: Document
class ExtVM: public ExtVMFace
{
public:
ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code):
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code, _s.m_previousBlock, _s.m_currentBlock, _s.m_currentNumber), m_s(_s), m_origCache(_s.m_cache)
{
m_s.ensureCached(_myAddress, true, true);
}
u256 store(u256 _n)
{
return m_s.storage(myAddress, _n);
}
void setStore(u256 _n, u256 _v)
{
m_s.setStorage(myAddress, _n, _v);
}
h160 create(u256 _endowment, u256* _gas, bytesConstRef _code)
{
// Increment associated nonce for sender.
m_s.noteSending(myAddress);
return m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin);
}
bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out)
{
return m_s.call(_receiveAddress, myAddress, _txValue, gasPrice, _txData, _gas, _out, origin);
}
u256 balance(Address _a) { return m_s.balance(_a); }
void subBalance(u256 _a) { m_s.subBalance(myAddress, _a); }
u256 txCount(Address _a) { return m_s.transactionsFrom(_a); }
void suicide(Address _a)
{
m_s.addBalance(_a, m_s.balance(myAddress));
m_s.m_cache[myAddress].kill();
}
void revert()
{
m_s.m_cache = m_origCache;
}
private:
State& m_s;
std::map<Address, AddressState> m_origCache;
std::map<u256, u256>* m_store;
};
}

163
libethereum/State.cpp

@ -30,6 +30,7 @@
#include "Exceptions.h"
#include "Dagger.h"
#include "Defaults.h"
#include "ExtVM.h"
#include "VM.h"
using namespace std;
using namespace eth;
@ -609,171 +610,11 @@ map<u256, u256> State::storage(Address _id) const
bytes const& State::code(Address _contract) const
{
if (!addressHasCode(_contract))
return EmptyBytes;
return NullBytes;
ensureCached(_contract, true, false);
return m_cache[_contract].code();
}
Executive::~Executive()
{
// TODO: Make safe.
delete m_ext;
delete m_vm;
}
void Executive::setup(bytesConstRef _rlp)
{
// Entry point for a user-executed transaction.
m_t = Transaction(_rlp);
auto sender = m_t.sender();
// Avoid invalid transactions.
auto nonceReq = m_s.transactionsFrom(sender);
if (m_t.nonce != nonceReq)
{
clog(StateChat) << "Invalid 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 < 10 * szabo)
{
clog(StateChat) << "Offered gas-price is too low.";
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.";
throw OutOfGas();
}
m_startGas = m_t.gas;
u256 cost = m_t.value + m_t.gas * m_t.gasPrice;
// Avoid unaffordable transactions.
if (m_s.balance(sender) < cost)
{
clog(StateChat) << "Not enough cash.";
throw NotEnoughCash();
}
// Increment associated nonce for sender.
m_s.noteSending(sender);
// Pay...
// cnote << "Paying" << formatBalance(cost) << "from sender (includes" << m_t.gas << "gas at" << formatBalance(m_t.gasPrice) << ")";
m_s.subBalance(sender, cost);
if (m_t.isCreation())
create(sender, m_t.value, m_t.gasPrice, m_t.gas - gasCost, &m_t.data, sender);
else
call(m_t.receiveAddress, sender, m_t.value, m_t.gasPrice, bytesConstRef(&m_t.data), m_t.gas - gasCost, sender);
}
void 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);
m_ext = new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &m_s.code(_receiveAddress));
}
else
m_endGas = _gas;
}
void 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, 0, h256(), h256());
// Execute _init.
m_vm = new VM(_gas);
m_ext = new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, _init, _init);
}
bool Executive::go(uint64_t _steps)
{
if (m_vm)
{
bool revert = false;
try
{
m_out = m_vm->go(*m_ext, _steps);
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->gas();
}
void Executive::finalize()
{
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
m_s.addBalance(m_ext->origin, m_endGas * m_ext->gasPrice);
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);
u256 gasSpent = (m_startGas - m_endGas) * m_ext->gasPrice;
/* unsigned c_feesKept = 8;
u256 feesEarned = gasSpent - (gasSpent / c_feesKept);
cnote << "Transferring" << (100.0 - 100.0 / c_feesKept) << "% of" << formatBalance(gasSpent) << "=" << formatBalance(feesEarned) << "to miner (" << formatBalance(gasSpent - feesEarned) << "is burnt).";
*/
u256 feesEarned = gasSpent;
// cnote << "Transferring" << formatBalance(gasSpent) << "to miner.";
m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned);
}
void State::execute(bytesConstRef _rlp)
{
Executive e(*this);

98
libethereum/State.h

@ -35,6 +35,7 @@
#include "FeeStructure.h"
#include "Dagger.h"
#include "ExtVMFace.h"
#include "Executive.h"
namespace eth
{
@ -44,52 +45,8 @@ class BlockChain;
extern u256 c_genesisDifficulty;
std::map<Address, AddressState> const& genesisState();
static const std::map<u256, u256> EmptyMapU256U256;
static const bytes EmptyBytes;
struct StateChat: public LogChannel { static const char* name() { return "=S="; } static const int verbosity = 4; };
class VM;
class ExtVM;
class State;
class Executive
{
public:
Executive(State& _s): m_s(_s) {}
~Executive();
void setup(bytesConstRef _transaction);
void create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress);
void call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress);
bool go(uint64_t _steps = (unsigned)-1);
void finalize();
Transaction const& t() const { return m_t; }
u256 gas() const;
bytesConstRef out() const { return m_out; }
h160 newAddress() const { return m_newAddress; }
VM const& vm() const { return *m_vm; }
State const& state() const { return m_s; }
ExtVM const& ext() const { return *m_ext; }
private:
State& m_s;
ExtVM* m_ext = nullptr; // TODO: make safe.
VM* m_vm = nullptr;
bytesConstRef m_out;
Address m_newAddress;
Transaction m_t;
u256 m_startGas;
u256 m_endGas;
};
/**
* @brief Model of the current state of the ledger.
* Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block).
@ -292,57 +249,8 @@ private:
friend std::ostream& operator<<(std::ostream& _out, State const& _s);
};
class ExtVM: public ExtVMFace
{
public:
ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code):
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code, _s.m_previousBlock, _s.m_currentBlock, _s.m_currentNumber), m_s(_s), m_origCache(_s.m_cache)
{
m_s.ensureCached(_myAddress, true, true);
}
u256 store(u256 _n)
{
return m_s.storage(myAddress, _n);
}
void setStore(u256 _n, u256 _v)
{
m_s.setStorage(myAddress, _n, _v);
}
h160 create(u256 _endowment, u256* _gas, bytesConstRef _code)
{
// Increment associated nonce for sender.
m_s.noteSending(myAddress);
return m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin);
}
bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out)
{
return m_s.call(_receiveAddress, myAddress, _txValue, gasPrice, _txData, _gas, _out, origin);
}
u256 balance(Address _a) { return m_s.balance(_a); }
void subBalance(u256 _a) { m_s.subBalance(myAddress, _a); }
u256 txCount(Address _a) { return m_s.transactionsFrom(_a); }
void suicide(Address _a)
{
m_s.addBalance(_a, m_s.balance(myAddress));
m_s.m_cache[myAddress].kill();
}
void revert()
{
m_s.m_cache = m_origCache;
}
private:
State& m_s;
std::map<Address, AddressState> m_origCache;
std::map<u256, u256>* m_store;
};
// TODO: Update for latest AddressState/StateTrie changes.
// trie should always be used as base. AddressState just contains overlay.
inline std::ostream& operator<<(std::ostream& _out, State const& _s)
{
_out << "--- " << _s.rootHash() << std::endl;

1
test/vm.cpp

@ -21,6 +21,7 @@
*/
#include <fstream>
#include <cstdint>
#include <libethcore/Log.h>
#include <libethereum/ExtVMFace.h>
#include <libethereum/Transaction.h>

Loading…
Cancel
Save