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()); restoreGeometry(s.value("geometry").toByteArray());
restoreState(s.value("windowState").toByteArray()); restoreState(s.value("windowState").toByteArray());
QByteArray b = s.value("address").toByteArray(); QByteArray b = s.value("address").toByteArray();
if (b.isEmpty()) if (b.isEmpty())
m_myKeys.append(KeyPair::create()); 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/>Coinbase: <b>" << pretty(info.coinbaseAddress).toStdString() << "</b> " << info.coinbaseAddress;
s << "<br/>State: <b>" << info.stateRoot << "</b>"; s << "<br/>State: <b>" << info.stateRoot << "</b>";
s << "<br/>Nonce: <b>" << info.nonce << "</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>"; s << "<br/>Uncles: <b>" << block[2].itemCount() << "</b> @<b>" << info.sha3Uncles << "</b>";
} }
else 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. * @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree.
* This version uses an LDB backend * 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> template <class DB>
class GenericTrieDB class GenericTrieDB
@ -101,6 +112,11 @@ public:
void init(); 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(); } 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. 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() {} 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) void BlockChain::import(bytes const& _block, Overlay const& _db)
{ {
// VERIFY: populates from the block and checks the block is internally coherent. // VERIFY: populates from the block and checks the block is internally coherent.
BlockInfo bi(&_block); BlockInfo bi;
#if ETH_CATCH #if ETH_CATCH
try try
#endif #endif
{ {
bi.populate(&_block);
bi.verifyInternals(&_block); bi.verifyInternals(&_block);
} }
#if ETH_CATCH #if ETH_CATCH

86
libethereum/BlockInfo.cpp

@ -21,6 +21,7 @@
#include <libethcore/Common.h> #include <libethcore/Common.h>
#include <libethcore/RLP.h> #include <libethcore/RLP.h>
#include <libethcore/TrieDB.h>
#include "Dagger.h" #include "Dagger.h"
#include "Exceptions.h" #include "Exceptions.h"
#include "State.h" #include "State.h"
@ -60,7 +61,7 @@ bytes BlockInfo::createGenesisBlock()
stateRoot = state.root(); 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);
block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList);
return block.out(); return block.out();
@ -75,7 +76,7 @@ h256 BlockInfo::headerHashWithoutNonce() const
void BlockInfo::fillStream(RLPStream& _s, bool _nonce) 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) if (_nonce)
_s << nonce; _s << nonce;
} }
@ -95,16 +96,30 @@ void BlockInfo::populateFromHeader(RLP const& _header)
sha3Uncles = _header[field = 1].toHash<h256>(); sha3Uncles = _header[field = 1].toHash<h256>();
coinbaseAddress = _header[field = 2].toHash<Address>(); coinbaseAddress = _header[field = 2].toHash<Address>();
stateRoot = _header[field = 3].toHash<h256>(); stateRoot = _header[field = 3].toHash<h256>();
sha3Transactions = _header[field = 4].toHash<h256>(); transactionsRoot = _header[field = 4].toHash<h256>();
difficulty = _header[field = 5].toInt<u256>(); difficulty = _header[field = 5].toInt<u256>();
timestamp = _header[field = 6].toInt<u256>(); number = _header[field = 6].toInt<u256>();
extraData = _header[field = 7].toBytes(); minGasPrice = _header[field = 7].toInt<u256>();
nonce = _header[field = 8].toHash<h256>(); 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&) catch (RLPException const&)
{ {
throw InvalidBlockHeaderFormat(field, _header[field].data()); 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) void BlockInfo::populate(bytesConstRef _block)
@ -121,22 +136,55 @@ void BlockInfo::populate(bytesConstRef _block)
throw InvalidBlockFormat(1, root[1].data()); throw InvalidBlockFormat(1, root[1].data());
if (!root[2].isList()) if (!root[2].isList())
throw InvalidBlockFormat(2, root[2].data()); 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 void BlockInfo::verifyInternals(bytesConstRef _block) const
{ {
RLP root(_block); RLP root(_block);
if (sha3Transactions != sha3(root[1].data())) u256 mgp = 0;
throw InvalidTransactionsHash(sha3Transactions, sha3(root[1].data()));
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())) if (sha3Uncles != sha3(root[2].data()))
throw InvalidUnclesHash(); 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 u256 BlockInfo::calculateDifficulty(BlockInfo const& _parent) const
{ {
if (!parentHash) if (!parentHash)
@ -151,7 +199,19 @@ void BlockInfo::verifyParent(BlockInfo const& _parent) const
if (difficulty != calculateDifficulty(_parent)) if (difficulty != calculateDifficulty(_parent))
throw InvalidDifficulty(); throw InvalidDifficulty();
if (gasLimit != calculateGasLimit(_parent))
throw InvalidGasLimit();
// Check timestamp is after previous timestamp. // Check timestamp is after previous timestamp.
if (parentHash && _parent.timestamp > timestamp) if (parentHash)
throw InvalidTimestamp(); {
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; h256 sha3Uncles;
Address coinbaseAddress; Address coinbaseAddress;
h256 stateRoot; h256 stateRoot;
h256 sha3Transactions; h256 transactionsRoot;
u256 difficulty; u256 difficulty;
u256 number;
u256 minGasPrice;
u256 gasLimit;
u256 gasUsed;
u256 timestamp; u256 timestamp;
bytes extraData; bytes extraData;
h256 nonce; h256 nonce;
@ -54,8 +58,12 @@ public:
sha3Uncles == _cmp.sha3Uncles && sha3Uncles == _cmp.sha3Uncles &&
coinbaseAddress == _cmp.coinbaseAddress && coinbaseAddress == _cmp.coinbaseAddress &&
stateRoot == _cmp.stateRoot && stateRoot == _cmp.stateRoot &&
sha3Transactions == _cmp.sha3Transactions && transactionsRoot == _cmp.transactionsRoot &&
difficulty == _cmp.difficulty && difficulty == _cmp.difficulty &&
number == _cmp.number &&
minGasPrice == _cmp.minGasPrice &&
gasLimit == _cmp.gasLimit &&
gasUsed == _cmp.gasUsed &&
timestamp == _cmp.timestamp && timestamp == _cmp.timestamp &&
extraData == _cmp.extraData && extraData == _cmp.extraData &&
nonce == _cmp.nonce; nonce == _cmp.nonce;
@ -67,8 +75,10 @@ public:
void populate(bytesConstRef _block); void populate(bytesConstRef _block);
void verifyInternals(bytesConstRef _block) const; void verifyInternals(bytesConstRef _block) const;
void verifyParent(BlockInfo const& _parent) 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. /// No-nonce sha3 of the header only.
h256 headerHashWithoutNonce() const; h256 headerHashWithoutNonce() const;
@ -84,8 +94,8 @@ private:
inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi) inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi)
{ {
_out << _bi.hash << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.sha3Transactions << " " << _out << _bi.hash << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.transactionsRoot << " " <<
_bi.difficulty << " " << _bi.timestamp << " " << _bi.nonce; _bi.difficulty << " " << _bi.number << " " << _bi.minGasPrice << " " << _bi.gasLimit << " " << _bi.gasUsed << " " << _bi.timestamp << " " << _bi.nonce;
return _out; return _out;
} }

2
libethereum/Client.h

@ -125,7 +125,7 @@ public:
/// Get the object representing the current canonical blockchain. /// Get the object representing the current canonical blockchain.
BlockChain const& blockChain() const { return m_bc; } BlockChain const& blockChain() const { return m_bc; }
/// Get a map containing each of the pending transactions. /// 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; } 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 NotEnoughCash: public Exception {};
class GasPriceTooLow: public Exception {}; class GasPriceTooLow: public Exception {};
class BlockGasLimitReached: public Exception {};
class NoSuchContract: public Exception {}; class NoSuchContract: public Exception {};
class ContractAddressCollision: public Exception {}; class ContractAddressCollision: public Exception {};
class FeeTooSmall: public Exception {}; class FeeTooSmall: public Exception {};
class TooMuchGasUsed: public Exception {};
class ExtraDataTooBig: public Exception {};
class InvalidSignature: 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 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 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 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 InvalidUnclesHash: public Exception {};
class InvalidUncle: public Exception {}; class InvalidUncle: public Exception {};
class UncleNotAnUncle: public Exception {};
class DuplicateUncleNonce: public Exception {};
class InvalidStateRoot: 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 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 InvalidTransaction: public Exception {};
class InvalidDifficulty: 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 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 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 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 InvalidParentHash: public Exception {};
class InvalidNumber: public Exception {};
class InvalidContractAddress: public Exception {}; class InvalidContractAddress: public Exception {};
} }

23
libethereum/Executive.cpp

@ -33,6 +33,11 @@ Executive::~Executive()
delete m_vm; delete m_vm;
} }
u256 Executive::gasUsed() const
{
return m_t.gas - m_endGas;
}
void Executive::setup(bytesConstRef _rlp) void Executive::setup(bytesConstRef _rlp)
{ {
// Entry point for a user-executed transaction. // 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. // 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."; clog(StateChat) << "Offered gas-price is too low.";
throw GasPriceTooLow(); throw GasPriceTooLow();
@ -64,8 +69,6 @@ void Executive::setup(bytesConstRef _rlp)
throw OutOfGas(); throw OutOfGas();
} }
m_startGas = m_t.gas;
u256 cost = m_t.value + m_t.gas * m_t.gasPrice; u256 cost = m_t.value + m_t.gas * m_t.gasPrice;
// Avoid unaffordable transactions. // Avoid unaffordable transactions.
@ -169,19 +172,19 @@ u256 Executive::gas() const
void Executive::finalize() 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()) if (m_t.isCreation() && m_newAddress && m_out.size())
// non-reverted creation - put code in place. // non-reverted creation - put code in place.
m_s.m_cache[m_newAddress].setCode(m_out); 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; /* unsigned c_feesKept = 8;
u256 feesEarned = gasSpent - (gasSpent / c_feesKept); u256 feesEarned = gasSpentInEth - (gasSpentInEth / c_feesKept);
cnote << "Transferring" << (100.0 - 100.0 / c_feesKept) << "% of" << formatBalance(gasSpent) << "=" << formatBalance(feesEarned) << "to miner (" << formatBalance(gasSpent - feesEarned) << "is burnt)."; 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."; // cnote << "Transferring" << formatBalance(gasSpent) << "to miner.";
m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned); 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); void call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress);
bool go(uint64_t _steps = (unsigned)-1); bool go(uint64_t _steps = (unsigned)-1);
void finalize(); void finalize();
u256 gasUsed() const;
Transaction const& t() const { return m_t; } Transaction const& t() const { return m_t; }
@ -62,8 +63,6 @@ private:
Address m_newAddress; Address m_newAddress;
Transaction m_t; Transaction m_t;
u256 m_startGas;
u256 m_endGas; u256 m_endGas;
}; };

2
libethereum/ExtVM.h

@ -34,7 +34,7 @@ class ExtVM: public ExtVMFace
{ {
public: public:
ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code): 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); m_s.ensureCached(_myAddress, true, true);
} }

11
libethereum/ExtVMFace.h

@ -35,13 +35,12 @@ class ExtVMFace
public: public:
ExtVMFace() {} ExtVMFace() {}
ExtVMFace(BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber): ExtVMFace(BlockInfo const& _previousBlock, BlockInfo const& _currentBlock):
previousBlock(_previousBlock), previousBlock(_previousBlock),
currentBlock(_currentBlock), currentBlock(_currentBlock)
currentNumber(_currentNumber)
{} {}
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), myAddress(_myAddress),
caller(_caller), caller(_caller),
origin(_origin), origin(_origin),
@ -50,8 +49,7 @@ public:
data(_data), data(_data),
code(_code), code(_code),
previousBlock(_previousBlock), previousBlock(_previousBlock),
currentBlock(_currentBlock), currentBlock(_currentBlock)
currentNumber(_currentNumber)
{} {}
#pragma warning(push) #pragma warning(push)
@ -81,7 +79,6 @@ public:
bytesConstRef code; bytesConstRef code;
BlockInfo previousBlock; ///< The current block's information. BlockInfo previousBlock; ///< The current block's information.
BlockInfo currentBlock; ///< 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): State::State(Address _coinbaseAddress, Overlay const& _db):
m_db(_db), m_db(_db),
m_state(&m_db), m_state(&m_db),
m_transactionManifest(&m_db),
m_ourAddress(_coinbaseAddress) m_ourAddress(_coinbaseAddress)
{ {
m_blockReward = 1500 * finney; m_blockReward = 1500 * finney;
@ -93,10 +94,10 @@ State::State(State const& _s):
m_state(&m_db, _s.m_state.root()), m_state(&m_db, _s.m_state.root()),
m_transactions(_s.m_transactions), m_transactions(_s.m_transactions),
m_transactionSet(_s.m_transactionSet), m_transactionSet(_s.m_transactionSet),
m_transactionManifest(&m_db, _s.m_transactionManifest.root()),
m_cache(_s.m_cache), m_cache(_s.m_cache),
m_previousBlock(_s.m_previousBlock), m_previousBlock(_s.m_previousBlock),
m_currentBlock(_s.m_currentBlock), m_currentBlock(_s.m_currentBlock),
m_currentNumber(_s.m_currentNumber),
m_ourAddress(_s.m_ourAddress), m_ourAddress(_s.m_ourAddress),
m_blockReward(_s.m_blockReward) 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_state.open(&m_db, _s.m_state.root());
m_transactions = _s.m_transactions; m_transactions = _s.m_transactions;
m_transactionSet = _s.m_transactionSet; m_transactionSet = _s.m_transactionSet;
m_transactionManifest.open(&m_db, _s.m_transactionManifest.root());
m_cache = _s.m_cache; m_cache = _s.m_cache;
m_previousBlock = _s.m_previousBlock; m_previousBlock = _s.m_previousBlock;
m_currentBlock = _s.m_currentBlock; m_currentBlock = _s.m_currentBlock;
m_currentNumber = _s.m_currentNumber;
m_ourAddress = _s.m_ourAddress; m_ourAddress = _s.m_ourAddress;
m_blockReward = _s.m_blockReward; m_blockReward = _s.m_blockReward;
return *this; 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. // Our state is good - we just need to move on to next.
m_previousBlock = m_currentBlock; m_previousBlock = m_currentBlock;
resetCurrent(); resetCurrent();
m_currentNumber++;
ret = true; ret = true;
} }
else if (bi == m_previousBlock) 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. // Iterate through in reverse, playing back each of the blocks.
for (auto it = chain.rbegin(); it != chain.rend(); ++it) 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(); resetCurrent();
ret = true; ret = true;
} }
@ -230,16 +229,18 @@ void State::resetCurrent()
{ {
m_transactions.clear(); m_transactions.clear();
m_transactionSet.clear(); m_transactionSet.clear();
m_transactionManifest.init();
m_cache.clear(); m_cache.clear();
m_currentBlock = BlockInfo(); m_currentBlock = BlockInfo();
m_currentBlock.coinbaseAddress = m_ourAddress; m_currentBlock.coinbaseAddress = m_ourAddress;
m_currentBlock.stateRoot = m_previousBlock.stateRoot; m_currentBlock.timestamp = time(0);
m_currentBlock.parentHash = m_previousBlock.hash; m_currentBlock.transactionsRoot = h256();
m_currentBlock.sha3Transactions = h256();
m_currentBlock.sha3Uncles = h256(); m_currentBlock.sha3Uncles = h256();
m_currentBlock.minGasPrice = 10 * szabo;
m_currentBlock.populateFromParent(m_previousBlock);
// Update timestamp according to clock. // Update timestamp according to clock.
m_currentBlock.timestamp = time(0); // TODO: check.
m_state.setRoot(m_currentBlock.stateRoot); m_state.setRoot(m_currentBlock.stateRoot);
} }
@ -316,13 +317,20 @@ bool State::sync(TransactionQueue& _tq)
return ret; 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 try
{ {
m_currentBlock.populate(_block); m_currentBlock.populate(_block);
m_currentBlock.verifyInternals(_block); m_currentBlock.verifyInternals(_block);
return playback(_block, BlockInfo(), _fullCommit); return playbackRaw(_block, BlockInfo(), _fullCommit);
} }
catch (...) 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) u256 State::playbackRaw(bytesConstRef _block, 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)
{ {
if (m_currentBlock.parentHash != m_previousBlock.hash) if (m_currentBlock.parentHash != m_previousBlock.hash)
throw InvalidParentHash(); throw InvalidParentHash();
@ -347,23 +348,44 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _
// cnote << "playback begins:" << m_state.root(); // cnote << "playback begins:" << m_state.root();
// cnote << m_state; // cnote << m_state;
if (_fullCommit)
m_transactionManifest.init();
// All ok with the block generally. Play back the transactions now... // All ok with the block generally. Play back the transactions now...
for (auto const& i: RLP(_block)[1]) unsigned i = 0;
execute(i.data()); 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. // Initialise total difficulty calculation.
u256 tdIncrease = m_currentBlock.difficulty; u256 tdIncrease = m_currentBlock.difficulty;
// Check uncles & apply their rewards to state. // Check uncles & apply their rewards to state.
// TODO: Check for uniqueness of uncles. // TODO: Check for uniqueness of uncles.
set<h256> nonces = { m_currentBlock.nonce };
Addresses rewarded; Addresses rewarded;
for (auto const& i: RLP(_block)[2]) for (auto const& i: RLP(_block)[2])
{ {
BlockInfo uncle = BlockInfo::fromHeader(i.data()); BlockInfo uncle = BlockInfo::fromHeader(i.data());
if (m_previousBlock.parentHash != uncle.parentHash) if (m_previousBlock.parentHash != uncle.parentHash)
throw InvalidUncle(); throw UncleNotAnUncle();
if (nonces.count(uncle.nonce))
throw DuplicateUncleNonce();
if (_grandParent) if (_grandParent)
uncle.verifyParent(_grandParent); uncle.verifyParent(_grandParent);
nonces.insert(uncle.nonce);
tdIncrease += uncle.difficulty; tdIncrease += uncle.difficulty;
rewarded.push_back(uncle.coinbaseAddress); 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). // (i.e. all the transactions we executed).
void State::commitToMine(BlockChain const& _bc) void State::commitToMine(BlockChain const& _bc)
{ {
if (m_currentBlock.sha3Transactions != h256() || m_currentBlock.sha3Uncles != h256()) if (m_currentBlock.sha3Uncles != h256())
{ {
Addresses uncleAddresses; Addresses uncleAddresses;
for (auto i: RLP(m_currentUncles)) for (auto i: RLP(m_currentUncles))
@ -439,15 +461,28 @@ void State::commitToMine(BlockChain const& _bc)
uncles.appendList(0); uncles.appendList(0);
applyRewards(uncleAddresses); 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()); RLPStream txs;
for (auto const& i: m_transactions) txs.appendList(m_transactions.size());
i.fillStream(txs); 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); txs.swapOut(m_currentTxs);
uncles.swapOut(m_currentUncles); uncles.swapOut(m_currentUncles);
m_currentBlock.sha3Transactions = sha3(m_currentTxs); m_currentBlock.transactionsRoot = m_transactionManifest.root();
m_currentBlock.sha3Uncles = sha3(m_currentUncles); 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. // 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(); return m_cache[_contract].code();
} }
void State::execute(bytesConstRef _rlp) u256 State::execute(bytesConstRef _rlp)
{ {
Executive e(*this); Executive e(*this);
{ e.setup(_rlp);
e.setup(_rlp);
e.go(); if (m_currentBlock.gasUsed + e.t().gas > m_currentBlock.gasLimit)
e.finalize(); throw BlockGasLimitReached(); // TODO: make sure this is handled.
}
e.go();
e.finalize();
commit();
// Add to the user-originated transactions that we've executed. // 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()); 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) 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 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. * @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). * 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. /// Execute a given transaction.
/// This will append @a _t to the transaction list and change the state accordingly. /// This will append @a _t to the transaction list and change the state accordingly.
void execute(bytes const& _rlp) { return execute(&_rlp); } u256 execute(bytes const& _rlp) { return execute(&_rlp); }
void execute(bytesConstRef _rlp); u256 execute(bytesConstRef _rlp);
/// Check if the address is in use. /// Check if the address is in use.
bool addressInUse(Address _address) const; bool addressInUse(Address _address) const;
@ -168,7 +184,7 @@ public:
h256 rootHash() const { return m_state.root(); } h256 rootHash() const { return m_state.root(); }
/// Get the list of pending transactions. /// 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. /// Execute all transactions within a given block.
/// @returns the additional total difficulty. /// @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(). /// 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. /// 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. /// 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. /// 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): // 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. // We assume all instrinsic fees are paid up before this point.
@ -220,20 +236,22 @@ private:
/// Finalise the block, applying the earned rewards. /// Finalise the block, applying the earned rewards.
void applyRewards(Addresses const& _uncleAddresses); void applyRewards(Addresses const& _uncleAddresses);
void refreshManifest(RLPStream* _txs = nullptr);
/// Unfinalise the block, unapplying the earned rewards. /// Unfinalise the block, unapplying the earned rewards.
void unapplyRewards(Addresses const& _uncleAddresses); void unapplyRewards(Addresses const& _uncleAddresses);
Overlay m_db; ///< Our overlay for the state tree. Overlay m_db; ///< Our overlay for the state tree.
TrieDB<Address, Overlay> m_state; ///< Our state tree, as an Overlay DB. 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. 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. 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_previousBlock; ///< The previous block's information.
BlockInfo m_currentBlock; ///< The current block's information. BlockInfo m_currentBlock; ///< The current block's information.
bytes m_currentBytes; ///< The current block. bytes m_currentBytes; ///< The current block.
uint m_currentNumber;
bytes m_currentTxs; bytes m_currentTxs;
bytes m_currentUncles; 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); m_stack.push_back(_ext.currentBlock.timestamp);
break; break;
case Instruction::NUMBER: case Instruction::NUMBER:
m_stack.push_back(_ext.currentNumber); m_stack.push_back(_ext.currentBlock.number);
break; break;
case Instruction::DIFFICULTY: case Instruction::DIFFICULTY:
m_stack.push_back(_ext.currentBlock.difficulty); m_stack.push_back(_ext.currentBlock.difficulty);

4
test/vm.cpp

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

Loading…
Cancel
Save