Browse Source

Protocol changes for PoC-5:

Transactions Trie.
LTMA for gasPrice.
New block header format.
Various additional checks.
New Genesis block.
cl-refactor
Gav Wood 11 years ago
parent
commit
ca65afe8f0
  1. 3
      alethzero/MainWin.cpp
  2. 16
      libethcore/TrieDB.h
  3. 4
      libethereum/BlockChain.cpp
  4. 86
      libethereum/BlockInfo.cpp
  5. 20
      libethereum/BlockInfo.h
  6. 2
      libethereum/Client.h
  7. 10
      libethereum/Exceptions.h
  8. 23
      libethereum/Executive.cpp
  9. 3
      libethereum/Executive.h
  10. 2
      libethereum/ExtVM.h
  11. 11
      libethereum/ExtVMFace.h
  12. 109
      libethereum/State.cpp
  13. 32
      libethereum/State.h
  14. 2
      libethereum/VM.h
  15. 4
      test/vm.cpp

3
alethzero/MainWin.cpp

@ -348,7 +348,6 @@ void Main::readSettings()
restoreGeometry(s.value("geometry").toByteArray());
restoreState(s.value("windowState").toByteArray());
QByteArray b = s.value("address").toByteArray();
if (b.isEmpty())
m_myKeys.append(KeyPair::create());
@ -561,7 +560,7 @@ void Main::on_blocks_currentItemChanged()
s << "<br/>Coinbase: <b>" << pretty(info.coinbaseAddress).toStdString() << "</b> " << info.coinbaseAddress;
s << "<br/>State: <b>" << info.stateRoot << "</b>";
s << "<br/>Nonce: <b>" << info.nonce << "</b>";
s << "<br/>Transactions: <b>" << block[1].itemCount() << "</b> @<b>" << info.sha3Transactions << "</b>";
s << "<br/>Transactions: <b>" << block[1].itemCount() << "</b> @<b>" << info.transactionsRoot << "</b>";
s << "<br/>Uncles: <b>" << block[2].itemCount() << "</b> @<b>" << info.sha3Uncles << "</b>";
}
else

16
libethcore/TrieDB.h

@ -87,6 +87,17 @@ extern const h256 c_shaNull;
/**
* @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree.
* This version uses an LDB backend
* Usage:
* @code
* GenericTrieDB<MyDB> t(&myDB);
* assert(t.isNull());
* t.init();
* assert(t.isEmpty());
* t.insert(x, y);
* assert(t.at(x) == y.toString());
* t.remove(x);
* assert(t.isEmpty());
* @endcode
*/
template <class DB>
class GenericTrieDB
@ -101,6 +112,11 @@ public:
void init();
void setRoot(h256 _root) { m_root = _root == h256() ? c_shaNull : _root; /*std::cout << "Setting root to " << _root << " (patched to " << m_root << ")" << std::endl;*/ if (!node(m_root).size()) throw RootNotFound(); }
/// True if the trie is uninitialised (i.e. that the DB doesn't contain the root node).
bool isNull() const { return !node(m_root).size(); }
/// True if the trie is initialised but empty (i.e. that the DB contains the root node which is empty).
bool isEmpty() const { return m_root == c_shaNull && node(m_root).size(); }
h256 root() const { assert(node(m_root).size()); h256 ret = (m_root == c_shaNull ? h256() : m_root); /*std::cout << "Returning root as " << ret << " (really " << m_root << ")" << std::endl;*/ return ret; } // patch the root in the case of the empty trie. TODO: handle this properly.
void debugPrint() {}

4
libethereum/BlockChain.cpp

@ -139,11 +139,13 @@ bool BlockChain::attemptImport(bytes const& _block, Overlay const& _stateDB)
void BlockChain::import(bytes const& _block, Overlay const& _db)
{
// VERIFY: populates from the block and checks the block is internally coherent.
BlockInfo bi(&_block);
BlockInfo bi;
#if ETH_CATCH
try
#endif
{
bi.populate(&_block);
bi.verifyInternals(&_block);
}
#if ETH_CATCH

86
libethereum/BlockInfo.cpp

@ -21,6 +21,7 @@
#include <libethcore/Common.h>
#include <libethcore/RLP.h>
#include <libethcore/TrieDB.h>
#include "Dagger.h"
#include "Exceptions.h"
#include "State.h"
@ -60,7 +61,7 @@ bytes BlockInfo::createGenesisBlock()
stateRoot = state.root();
}
block.appendList(9) << h256() << sha3EmptyList << h160() << stateRoot << sha3EmptyList << c_genesisDifficulty << (uint)0 << string() << sha3(bytes(1, 42));
block.appendList(13) << h256() << sha3EmptyList << h160() << stateRoot << h256() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (uint)0 << string() << sha3(bytes(1, 42));
block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList);
return block.out();
@ -75,7 +76,7 @@ h256 BlockInfo::headerHashWithoutNonce() const
void BlockInfo::fillStream(RLPStream& _s, bool _nonce) const
{
_s.appendList(_nonce ? 9 : 8) << parentHash << sha3Uncles << coinbaseAddress << stateRoot << sha3Transactions << difficulty << timestamp << extraData;
_s.appendList(_nonce ? 13 : 12) << parentHash << sha3Uncles << coinbaseAddress << stateRoot << transactionsRoot << difficulty << number << minGasPrice << gasLimit << gasUsed << timestamp << extraData;
if (_nonce)
_s << nonce;
}
@ -95,16 +96,30 @@ void BlockInfo::populateFromHeader(RLP const& _header)
sha3Uncles = _header[field = 1].toHash<h256>();
coinbaseAddress = _header[field = 2].toHash<Address>();
stateRoot = _header[field = 3].toHash<h256>();
sha3Transactions = _header[field = 4].toHash<h256>();
transactionsRoot = _header[field = 4].toHash<h256>();
difficulty = _header[field = 5].toInt<u256>();
timestamp = _header[field = 6].toInt<u256>();
extraData = _header[field = 7].toBytes();
nonce = _header[field = 8].toHash<h256>();
number = _header[field = 6].toInt<u256>();
minGasPrice = _header[field = 7].toInt<u256>();
gasLimit = _header[field = 8].toInt<u256>();
gasUsed = _header[field = 9].toInt<u256>();
timestamp = _header[field = 10].toInt<u256>();
extraData = _header[field = 11].toBytes();
nonce = _header[field = 12].toHash<h256>();
}
catch (RLPException const&)
{
throw InvalidBlockHeaderFormat(field, _header[field].data());
}
// check it hashes according to proof of work or that it's the genesis block.
if (parentHash && !Dagger::verify(headerHashWithoutNonce(), nonce, difficulty))
throw InvalidBlockNonce(headerHashWithoutNonce(), nonce, difficulty);
if (gasUsed > gasLimit)
throw TooMuchGasUsed();
if (number && extraData.size() > 1024)
throw ExtraDataTooBig();
}
void BlockInfo::populate(bytesConstRef _block)
@ -121,22 +136,55 @@ void BlockInfo::populate(bytesConstRef _block)
throw InvalidBlockFormat(1, root[1].data());
if (!root[2].isList())
throw InvalidBlockFormat(2, root[2].data());
// check it hashes according to proof of work or that it's the genesis block.
if (parentHash && !Dagger::verify(headerHashWithoutNonce(), nonce, difficulty))
throw InvalidBlockNonce(headerHashWithoutNonce(), nonce, difficulty);
}
void BlockInfo::verifyInternals(bytesConstRef _block) const
{
RLP root(_block);
if (sha3Transactions != sha3(root[1].data()))
throw InvalidTransactionsHash(sha3Transactions, sha3(root[1].data()));
u256 mgp = 0;
Overlay db;
GenericTrieDB<Overlay> t(&db);
t.init();
unsigned i = 0;
for (auto const& tr: root[1])
{
bytes k = rlp(i);
t.insert(&k, tr.data());
u256 gp = tr[0][1].toInt<u256>();
if (!i || mgp > gp)
mgp = gp;
++i;
}
if (transactionsRoot != t.root())
throw InvalidTransactionsHash(t.root(), transactionsRoot);
if (minGasPrice > mgp)
throw InvalidMinGasPrice();
if (sha3Uncles != sha3(root[2].data()))
throw InvalidUnclesHash();
}
void BlockInfo::populateFromParent(BlockInfo const& _parent)
{
stateRoot = _parent.stateRoot;
parentHash = _parent.hash;
number = _parent.number + 1;
gasLimit = calculateGasLimit(_parent);
gasUsed = 0;
difficulty = calculateDifficulty(_parent);
}
u256 BlockInfo::calculateGasLimit(BlockInfo const& _parent) const
{
if (!parentHash)
return 1000000;
else
return (_parent.gasLimit * (1024 - 1) + (_parent.gasUsed * 6 / 5)) / 1024;
}
u256 BlockInfo::calculateDifficulty(BlockInfo const& _parent) const
{
if (!parentHash)
@ -151,7 +199,19 @@ void BlockInfo::verifyParent(BlockInfo const& _parent) const
if (difficulty != calculateDifficulty(_parent))
throw InvalidDifficulty();
if (gasLimit != calculateGasLimit(_parent))
throw InvalidGasLimit();
// Check timestamp is after previous timestamp.
if (parentHash && _parent.timestamp > timestamp)
throw InvalidTimestamp();
if (parentHash)
{
if (parentHash != _parent.hash)
throw InvalidParentHash();
if (timestamp < _parent.timestamp)
throw InvalidTimestamp();
if (number != _parent.number + 1)
throw InvalidNumber();
}
}

20
libethereum/BlockInfo.h

@ -35,8 +35,12 @@ public:
h256 sha3Uncles;
Address coinbaseAddress;
h256 stateRoot;
h256 sha3Transactions;
h256 transactionsRoot;
u256 difficulty;
u256 number;
u256 minGasPrice;
u256 gasLimit;
u256 gasUsed;
u256 timestamp;
bytes extraData;
h256 nonce;
@ -54,8 +58,12 @@ public:
sha3Uncles == _cmp.sha3Uncles &&
coinbaseAddress == _cmp.coinbaseAddress &&
stateRoot == _cmp.stateRoot &&
sha3Transactions == _cmp.sha3Transactions &&
transactionsRoot == _cmp.transactionsRoot &&
difficulty == _cmp.difficulty &&
number == _cmp.number &&
minGasPrice == _cmp.minGasPrice &&
gasLimit == _cmp.gasLimit &&
gasUsed == _cmp.gasUsed &&
timestamp == _cmp.timestamp &&
extraData == _cmp.extraData &&
nonce == _cmp.nonce;
@ -67,8 +75,10 @@ public:
void populate(bytesConstRef _block);
void verifyInternals(bytesConstRef _block) const;
void verifyParent(BlockInfo const& _parent) const;
void populateFromParent(BlockInfo const& parent);
u256 calculateDifficulty(BlockInfo const& _bi) const;
u256 calculateDifficulty(BlockInfo const& _parent) const;
u256 calculateGasLimit(BlockInfo const& _parent) const;
/// No-nonce sha3 of the header only.
h256 headerHashWithoutNonce() const;
@ -84,8 +94,8 @@ private:
inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi)
{
_out << _bi.hash << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.sha3Transactions << " " <<
_bi.difficulty << " " << _bi.timestamp << " " << _bi.nonce;
_out << _bi.hash << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.transactionsRoot << " " <<
_bi.difficulty << " " << _bi.number << " " << _bi.minGasPrice << " " << _bi.gasLimit << " " << _bi.gasUsed << " " << _bi.timestamp << " " << _bi.nonce;
return _out;
}

2
libethereum/Client.h

@ -125,7 +125,7 @@ public:
/// Get the object representing the current canonical blockchain.
BlockChain const& blockChain() const { return m_bc; }
/// Get a map containing each of the pending transactions.
Transactions const& pending() const { return m_postMine.pending(); }
Transactions pending() const { return m_postMine.pending(); }
void setClientVersion(std::string const& _name) { m_clientVersion = _name; }

10
libethereum/Exceptions.h

@ -16,23 +16,33 @@ class OperandOutOfRange: public VMException { public: OperandOutOfRange(u256 _mi
class NotEnoughCash: public Exception {};
class GasPriceTooLow: public Exception {};
class BlockGasLimitReached: public Exception {};
class NoSuchContract: public Exception {};
class ContractAddressCollision: public Exception {};
class FeeTooSmall: public Exception {};
class TooMuchGasUsed: public Exception {};
class ExtraDataTooBig: public Exception {};
class InvalidSignature: public Exception {};
class InvalidTransactionFormat: public Exception { public: InvalidTransactionFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid transaction format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } };
class InvalidBlockFormat: public Exception { public: InvalidBlockFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } };
class InvalidBlockHeaderFormat: public Exception { public: InvalidBlockHeaderFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block header format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } };
class InvalidUnclesHash: public Exception {};
class InvalidUncle: public Exception {};
class UncleNotAnUncle: public Exception {};
class DuplicateUncleNonce: public Exception {};
class InvalidStateRoot: public Exception {};
class InvalidTransactionsHash: public Exception { public: InvalidTransactionsHash(h256 _head, h256 _real): m_head(_head), m_real(_real) {} h256 m_head; h256 m_real; virtual std::string description() const { return "Invalid transactions hash: header says: " + toHex(m_head.ref()) + " block is:" + toHex(m_real.ref()); } };
class InvalidTransaction: public Exception {};
class InvalidDifficulty: public Exception {};
class InvalidGasLimit: public Exception {};
class InvalidMinGasPrice: public Exception {};
class InvalidTransactionGasUsed: public Exception {};
class InvalidTransactionStateRoot: public Exception {};
class InvalidTimestamp: public Exception {};
class InvalidNonce: public Exception { public: InvalidNonce(u256 _required = 0, u256 _candidate = 0): required(_required), candidate(_candidate) {} u256 required; u256 candidate; virtual std::string description() const { return "Invalid nonce (r: " + toString(required) + " c:" + toString(candidate) + ")"; } };
class InvalidBlockNonce: public Exception { public: InvalidBlockNonce(h256 _h = h256(), h256 _n = h256(), u256 _d = 0): h(_h), n(_n), d(_d) {} h256 h; h256 n; u256 d; virtual std::string description() const { return "Invalid nonce (h: " + toString(h) + " n:" + toString(n) + " d:" + toString(d) + ")"; } };
class InvalidParentHash: public Exception {};
class InvalidNumber: public Exception {};
class InvalidContractAddress: public Exception {};
}

23
libethereum/Executive.cpp

@ -33,6 +33,11 @@ Executive::~Executive()
delete m_vm;
}
u256 Executive::gasUsed() const
{
return m_t.gas - m_endGas;
}
void Executive::setup(bytesConstRef _rlp)
{
// Entry point for a user-executed transaction.
@ -49,7 +54,7 @@ void Executive::setup(bytesConstRef _rlp)
}
// 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)
if (m_t.gasPrice < m_s.m_currentBlock.minGasPrice)
{
clog(StateChat) << "Offered gas-price is too low.";
throw GasPriceTooLow();
@ -64,8 +69,6 @@ void Executive::setup(bytesConstRef _rlp)
throw OutOfGas();
}
m_startGas = m_t.gas;
u256 cost = m_t.value + m_t.gas * m_t.gasPrice;
// Avoid unaffordable transactions.
@ -169,19 +172,19 @@ u256 Executive::gas() const
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;
// 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);
u256 gasSpentInEth = (m_t.gas - 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 = 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 = gasSpent;
u256 feesEarned = gasSpentInEth;
// cnote << "Transferring" << formatBalance(gasSpent) << "to miner.";
m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned);
}

3
libethereum/Executive.h

@ -42,6 +42,7 @@ public:
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();
u256 gasUsed() const;
Transaction const& t() const { return m_t; }
@ -62,8 +63,6 @@ private:
Address m_newAddress;
Transaction m_t;
u256 m_startGas;
u256 m_endGas;
};

2
libethereum/ExtVM.h

@ -34,7 +34,7 @@ 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)
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code, _s.m_previousBlock, _s.m_currentBlock), m_s(_s), m_origCache(_s.m_cache)
{
m_s.ensureCached(_myAddress, true, true);
}

11
libethereum/ExtVMFace.h

@ -35,13 +35,12 @@ class ExtVMFace
public:
ExtVMFace() {}
ExtVMFace(BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
ExtVMFace(BlockInfo const& _previousBlock, BlockInfo const& _currentBlock):
previousBlock(_previousBlock),
currentBlock(_currentBlock),
currentNumber(_currentNumber)
currentBlock(_currentBlock)
{}
ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock):
myAddress(_myAddress),
caller(_caller),
origin(_origin),
@ -50,8 +49,7 @@ public:
data(_data),
code(_code),
previousBlock(_previousBlock),
currentBlock(_currentBlock),
currentNumber(_currentNumber)
currentBlock(_currentBlock)
{}
#pragma warning(push)
@ -81,7 +79,6 @@ public:
bytesConstRef code;
BlockInfo previousBlock; ///< The current block's information.
BlockInfo currentBlock; ///< The current block's information.
uint currentNumber;
};
}

109
libethereum/State.cpp

@ -70,6 +70,7 @@ Overlay State::openDB(std::string _path, bool _killExisting)
State::State(Address _coinbaseAddress, Overlay const& _db):
m_db(_db),
m_state(&m_db),
m_transactionManifest(&m_db),
m_ourAddress(_coinbaseAddress)
{
m_blockReward = 1500 * finney;
@ -93,10 +94,10 @@ State::State(State const& _s):
m_state(&m_db, _s.m_state.root()),
m_transactions(_s.m_transactions),
m_transactionSet(_s.m_transactionSet),
m_transactionManifest(&m_db, _s.m_transactionManifest.root()),
m_cache(_s.m_cache),
m_previousBlock(_s.m_previousBlock),
m_currentBlock(_s.m_currentBlock),
m_currentNumber(_s.m_currentNumber),
m_ourAddress(_s.m_ourAddress),
m_blockReward(_s.m_blockReward)
{
@ -108,10 +109,10 @@ State& State::operator=(State const& _s)
m_state.open(&m_db, _s.m_state.root());
m_transactions = _s.m_transactions;
m_transactionSet = _s.m_transactionSet;
m_transactionManifest.open(&m_db, _s.m_transactionManifest.root());
m_cache = _s.m_cache;
m_previousBlock = _s.m_previousBlock;
m_currentBlock = _s.m_currentBlock;
m_currentNumber = _s.m_currentNumber;
m_ourAddress = _s.m_ourAddress;
m_blockReward = _s.m_blockReward;
return *this;
@ -179,7 +180,6 @@ bool State::sync(BlockChain const& _bc, h256 _block)
// Our state is good - we just need to move on to next.
m_previousBlock = m_currentBlock;
resetCurrent();
m_currentNumber++;
ret = true;
}
else if (bi == m_previousBlock)
@ -205,9 +205,8 @@ bool State::sync(BlockChain const& _bc, h256 _block)
// Iterate through in reverse, playing back each of the blocks.
for (auto it = chain.rbegin(); it != chain.rend(); ++it)
playback(_bc.block(*it), true);
trustedPlayback(_bc.block(*it), true);
m_currentNumber = _bc.details(_block).number + 1;
resetCurrent();
ret = true;
}
@ -230,16 +229,18 @@ void State::resetCurrent()
{
m_transactions.clear();
m_transactionSet.clear();
m_transactionManifest.init();
m_cache.clear();
m_currentBlock = BlockInfo();
m_currentBlock.coinbaseAddress = m_ourAddress;
m_currentBlock.stateRoot = m_previousBlock.stateRoot;
m_currentBlock.parentHash = m_previousBlock.hash;
m_currentBlock.sha3Transactions = h256();
m_currentBlock.timestamp = time(0);
m_currentBlock.transactionsRoot = h256();
m_currentBlock.sha3Uncles = h256();
m_currentBlock.minGasPrice = 10 * szabo;
m_currentBlock.populateFromParent(m_previousBlock);
// Update timestamp according to clock.
m_currentBlock.timestamp = time(0);
// TODO: check.
m_state.setRoot(m_currentBlock.stateRoot);
}
@ -316,13 +317,20 @@ bool State::sync(TransactionQueue& _tq)
return ret;
}
u256 State::playback(bytesConstRef _block, bool _fullCommit)
u256 State::playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent, bool _fullCommit)
{
m_currentBlock = _bi;
m_previousBlock = _parent;
return playbackRaw(_block, _grandParent, _fullCommit);
}
u256 State::trustedPlayback(bytesConstRef _block, bool _fullCommit)
{
try
{
m_currentBlock.populate(_block);
m_currentBlock.verifyInternals(_block);
return playback(_block, BlockInfo(), _fullCommit);
return playbackRaw(_block, BlockInfo(), _fullCommit);
}
catch (...)
{
@ -332,14 +340,7 @@ u256 State::playback(bytesConstRef _block, bool _fullCommit)
}
}
u256 State::playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent, bool _fullCommit)
{
m_currentBlock = _bi;
m_previousBlock = _parent;
return playback(_block, _grandParent, _fullCommit);
}
u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _fullCommit)
u256 State::playbackRaw(bytesConstRef _block, BlockInfo const& _grandParent, bool _fullCommit)
{
if (m_currentBlock.parentHash != m_previousBlock.hash)
throw InvalidParentHash();
@ -347,23 +348,44 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _
// cnote << "playback begins:" << m_state.root();
// cnote << m_state;
if (_fullCommit)
m_transactionManifest.init();
// All ok with the block generally. Play back the transactions now...
for (auto const& i: RLP(_block)[1])
execute(i.data());
unsigned i = 0;
for (auto const& tr: RLP(_block)[1])
{
execute(tr[0].data());
if (tr[1].toInt<u256>() != m_state.root())
throw InvalidTransactionStateRoot();
if (tr[2].toInt<u256>() != m_currentBlock.gasUsed)
throw InvalidTransactionGasUsed();
if (_fullCommit)
{
bytes k = rlp(i);
m_transactionManifest.insert(&k, tr.data());
}
++i;
}
// Initialise total difficulty calculation.
u256 tdIncrease = m_currentBlock.difficulty;
// Check uncles & apply their rewards to state.
// TODO: Check for uniqueness of uncles.
set<h256> nonces = { m_currentBlock.nonce };
Addresses rewarded;
for (auto const& i: RLP(_block)[2])
{
BlockInfo uncle = BlockInfo::fromHeader(i.data());
if (m_previousBlock.parentHash != uncle.parentHash)
throw InvalidUncle();
throw UncleNotAnUncle();
if (nonces.count(uncle.nonce))
throw DuplicateUncleNonce();
if (_grandParent)
uncle.verifyParent(_grandParent);
nonces.insert(uncle.nonce);
tdIncrease += uncle.difficulty;
rewarded.push_back(uncle.coinbaseAddress);
}
@ -407,7 +429,7 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _
// (i.e. all the transactions we executed).
void State::commitToMine(BlockChain const& _bc)
{
if (m_currentBlock.sha3Transactions != h256() || m_currentBlock.sha3Uncles != h256())
if (m_currentBlock.sha3Uncles != h256())
{
Addresses uncleAddresses;
for (auto i: RLP(m_currentUncles))
@ -439,15 +461,28 @@ void State::commitToMine(BlockChain const& _bc)
uncles.appendList(0);
applyRewards(uncleAddresses);
if (m_transactionManifest.isNull())
m_transactionManifest.init();
else
while(!m_transactionManifest.isEmpty())
m_transactionManifest.remove((*m_transactionManifest.begin()).first);
RLPStream txs(m_transactions.size());
for (auto const& i: m_transactions)
i.fillStream(txs);
RLPStream txs;
txs.appendList(m_transactions.size());
for (unsigned i = 0; i < m_transactions.size(); ++i)
{
RLPStream k;
k << i;
RLPStream v;
m_transactions[i].fillStream(v);
m_transactionManifest.insert(&k.out(), &v.out());
txs.appendRaw(v.out());
}
txs.swapOut(m_currentTxs);
uncles.swapOut(m_currentUncles);
m_currentBlock.sha3Transactions = sha3(m_currentTxs);
m_currentBlock.transactionsRoot = m_transactionManifest.root();
m_currentBlock.sha3Uncles = sha3(m_currentUncles);
// Commit any and all changes to the trie that are in the cache, then update the state root accordingly.
@ -615,18 +650,24 @@ bytes const& State::code(Address _contract) const
return m_cache[_contract].code();
}
void State::execute(bytesConstRef _rlp)
u256 State::execute(bytesConstRef _rlp)
{
Executive e(*this);
{
e.setup(_rlp);
e.go();
e.finalize();
}
e.setup(_rlp);
if (m_currentBlock.gasUsed + e.t().gas > m_currentBlock.gasLimit)
throw BlockGasLimitReached(); // TODO: make sure this is handled.
e.go();
e.finalize();
commit();
// Add to the user-originated transactions that we've executed.
m_transactions.push_back(e.t());
m_currentBlock.gasUsed += e.gasUsed();
m_transactions.push_back(TransactionReceipt(e.t(), m_state.root(), m_currentBlock.gasUsed));
m_transactionSet.insert(e.t().sha3());
return e.gasUsed();
}
bool State::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress)

32
libethereum/State.h

@ -47,6 +47,22 @@ std::map<Address, AddressState> const& genesisState();
struct StateChat: public LogChannel { static const char* name() { return "=S="; } static const int verbosity = 4; };
struct TransactionReceipt
{
TransactionReceipt(Transaction const& _t, h256 _root, u256 _gasUsed): transaction(_t), stateRoot(_root), gasUsed(_gasUsed) {}
void fillStream(RLPStream& _s) const
{
_s.appendList(3);
transaction.fillStream(_s);
_s << stateRoot << gasUsed;
}
Transaction transaction;
h256 stateRoot;
u256 gasUsed;
};
/**
* @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).
@ -118,8 +134,8 @@ public:
/// Execute a given transaction.
/// This will append @a _t to the transaction list and change the state accordingly.
void execute(bytes const& _rlp) { return execute(&_rlp); }
void execute(bytesConstRef _rlp);
u256 execute(bytes const& _rlp) { return execute(&_rlp); }
u256 execute(bytesConstRef _rlp);
/// Check if the address is in use.
bool addressInUse(Address _address) const;
@ -168,7 +184,7 @@ public:
h256 rootHash() const { return m_state.root(); }
/// Get the list of pending transactions.
Transactions const& pending() const { return m_transactions; }
Transactions pending() const { Transactions ret; for (auto const& t: m_transactions) ret.push_back(t.transaction); return ret; }
/// Execute all transactions within a given block.
/// @returns the additional total difficulty.
@ -197,11 +213,11 @@ private:
/// Execute the given block on our previous block. This will set up m_currentBlock first, then call the other playback().
/// Any failure will be critical.
u256 playback(bytesConstRef _block, bool _fullCommit);
u256 trustedPlayback(bytesConstRef _block, bool _fullCommit);
/// Execute the given block, assuming it corresponds to m_currentBlock. If _grandParent is passed, it will be used to check the uncles.
/// Throws on failure.
u256 playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _fullCommit);
u256 playbackRaw(bytesConstRef _block, BlockInfo const& _grandParent, bool _fullCommit);
// Two priviledged entry points for transaction processing used by the VM (these don't get added to the Transaction lists):
// We assume all instrinsic fees are paid up before this point.
@ -220,20 +236,22 @@ private:
/// Finalise the block, applying the earned rewards.
void applyRewards(Addresses const& _uncleAddresses);
void refreshManifest(RLPStream* _txs = nullptr);
/// Unfinalise the block, unapplying the earned rewards.
void unapplyRewards(Addresses const& _uncleAddresses);
Overlay m_db; ///< Our overlay for the state tree.
TrieDB<Address, Overlay> m_state; ///< Our state tree, as an Overlay DB.
Transactions m_transactions; ///< The current list of transactions that we've included in the state.
std::vector<TransactionReceipt> m_transactions; ///< The current list of transactions that we've included in the state.
std::set<h256> m_transactionSet; ///< The set of transaction hashes that we've included in the state.
GenericTrieDB<Overlay> m_transactionManifest; ///< The transactions trie; saved from the last commitToMine, or invalid/empty if commitToMine was never called.
mutable std::map<Address, AddressState> m_cache; ///< Our address cache. This stores the states of each address that has (or at least might have) been changed.
BlockInfo m_previousBlock; ///< The previous block's information.
BlockInfo m_currentBlock; ///< The current block's information.
bytes m_currentBytes; ///< The current block.
uint m_currentNumber;
bytes m_currentTxs;
bytes m_currentUncles;

2
libethereum/VM.h

@ -344,7 +344,7 @@ template <class Ext> eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps)
m_stack.push_back(_ext.currentBlock.timestamp);
break;
case Instruction::NUMBER:
m_stack.push_back(_ext.currentNumber);
m_stack.push_back(_ext.currentBlock.number);
break;
case Instruction::DIFFICULTY:
m_stack.push_back(_ext.currentBlock.difficulty);

4
test/vm.cpp

@ -41,8 +41,8 @@ class FakeExtVM: public ExtVMFace
public:
FakeExtVM()
{}
FakeExtVM(BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
ExtVMFace(Address(), Address(), Address(), 0, 1, bytesConstRef(), bytesConstRef(), _previousBlock, _currentBlock, _currentNumber)
FakeExtVM(BlockInfo const& _previousBlock, BlockInfo const& _currentBlock):
ExtVMFace(Address(), Address(), Address(), 0, 1, bytesConstRef(), bytesConstRef(), _previousBlock, _currentBlock)
{}
u256 store(u256 _n)

Loading…
Cancel
Save