Browse Source

State integration test.

Fixes to the FatTrie.
cl-refactor
Gav Wood 10 years ago
parent
commit
814349a840
  1. 47
      CMakeLists.txt
  2. 6
      libdevcore/FixedHash.h
  3. 2
      libdevcore/vector_ref.h
  4. 8
      libdevcrypto/MemoryDB.h
  5. 13
      libdevcrypto/OverlayDB.cpp
  6. 2
      libdevcrypto/OverlayDB.h
  7. 2
      libdevcrypto/TrieDB.h
  8. 2
      libethcore/CommonEth.cpp
  9. 36
      libethereum/Account.h
  10. 15
      libethereum/BlockChain.cpp
  11. 8
      libethereum/State.cpp
  12. 63
      libethereum/State.h
  13. 2
      test/CMakeLists.txt
  14. 36
      test/stateOriginal.cpp
  15. 20
      test/trie.cpp

47
CMakeLists.txt

@ -20,6 +20,7 @@ function(createDefaultCacheConfig)
set(JSONRPC ON CACHE BOOL "Build with jsonprc. default on")
set(EVMJIT OFF CACHE BOOL "Build a just-in-time compiler for EVM code (requires LLVM)")
set(FATDB OFF CACHE BOOL "Build with ability to list entries in the Trie. Doubles DB size, slows everything down, but good for looking at state diffs and trie contents.")
set(JUSTTESTS OFF CACHE BOOL "Build only for tests.")
endfunction()
@ -49,7 +50,7 @@ function(configureProject)
add_definitions(-DETH_FATDB)
endif()
if (HEADLESS)
if (HEADLESS OR JUSTTESTS)
add_definitions(-DETH_HEADLESS)
endif()
endfunction()
@ -153,8 +154,11 @@ if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
endif ()
add_subdirectory(libsolidity)
add_subdirectory(lllc)
add_subdirectory(solc)
if (NOT JUSTTESTS)
add_subdirectory(lllc)
add_subdirectory(solc)
endif()
if (JSONRPC)
add_subdirectory(libweb3jsonrpc)
@ -171,25 +175,30 @@ add_subdirectory(libethereum)
add_subdirectory(libwebthree)
add_subdirectory(test)
add_subdirectory(eth)
if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug")
add_subdirectory(exp)
endif ()
if (NOT JUSTTESTS)
# TODO check msvc
if(NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(neth)
endif ()
add_subdirectory(eth)
if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug")
add_subdirectory(exp)
endif ()
# TODO check msvc
if(NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(neth)
endif ()
if (NOT HEADLESS)
add_subdirectory(libnatspec)
add_subdirectory(libjsqrc)
add_subdirectory(alethzero)
add_subdirectory(third)
add_subdirectory(mix)
endif()
if (NOT HEADLESS)
add_subdirectory(libnatspec)
add_subdirectory(libjsqrc)
add_subdirectory(alethzero)
add_subdirectory(third)
add_subdirectory(mix)
endif()
enable_testing()

6
libdevcore/FixedHash.h

@ -65,16 +65,16 @@ public:
FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); }
/// Explicitly construct, copying from a byte array.
explicit FixedHash(bytes const& _b, ConstructFromHashType _t = FailIfDifferent) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min<unsigned>(_b.size(), N)); else if (_t != FailIfDifferent) { m_data.fill(0); auto c = std::min<unsigned>(_b.size(), N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _b[_t == AlignRight ? _b.size() - 1 - i : i]; } }
explicit FixedHash(bytes const& _b, ConstructFromHashType _t = FailIfDifferent) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min<unsigned>(_b.size(), N)); else { m_data.fill(0); if (_t != FailIfDifferent) { auto c = std::min<unsigned>(_b.size(), N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _b[_t == AlignRight ? _b.size() - 1 - i : i]; } } }
/// Explicitly construct, copying from a byte array.
explicit FixedHash(bytesConstRef _b, ConstructFromHashType _t = FailIfDifferent) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min<unsigned>(_b.size(), N)); else if (_t != FailIfDifferent) { m_data.fill(0); auto c = std::min<unsigned>(_b.size(), N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _b[_t == AlignRight ? _b.size() - 1 - i : i]; } }
explicit FixedHash(bytesConstRef _b, ConstructFromHashType _t = FailIfDifferent) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min<unsigned>(_b.size(), N)); else { m_data.fill(0); if (_t != FailIfDifferent) { auto c = std::min<unsigned>(_b.size(), N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _b[_t == AlignRight ? _b.size() - 1 - i : i]; } } }
/// Explicitly construct, copying from a bytes in memory with given pointer.
explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); }
/// Explicitly construct, copying from a string.
explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex): FixedHash(_t == FromHex ? fromHex(_s) : dev::asBytes(_s)) {}
explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex, ConstructFromHashType _ht = FailIfDifferent): FixedHash(_t == FromHex ? fromHex(_s) : dev::asBytes(_s), _ht) {}
/// Convert to arithmetic type.
operator Arith() const { return fromBigEndian<Arith>(m_data); }

2
libdevcore/vector_ref.h

@ -23,7 +23,7 @@ public:
vector_ref(typename std::conditional<std::is_const<_T>::value, std::vector<typename std::remove_const<_T>::type> const*, std::vector<_T>*>::type _data): m_data(_data->data()), m_count(_data->size()) {}
vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const&, std::string&>::type _data): m_data((_T*)_data.data()), m_count(_data.size() / sizeof(_T)) {}
#ifdef STORAGE_LEVELDB_INCLUDE_DB_H_
vector_ref(leveldb::Slice const& _s): m_data(_s.data()), m_count(_s.size() / sizeof(_T)) {}
vector_ref(leveldb::Slice const& _s): m_data(reinterpret_cast<_T*>(_s.data())), m_count(_s.size() / sizeof(_T)) {}
#endif
explicit operator bool() const { return m_data && m_count; }

8
libdevcrypto/MemoryDB.h

@ -51,14 +51,18 @@ public:
bool kill(h256 _h);
void purge();
bytes lookupAux(h256 _h) const { return asBytes(lookup(h256(sha3(_h).ref().cropped(16), h256::AlignRight))); }
void insertAux(h256 _h, bytesConstRef _v) { return insert(h256(sha3(_h).ref().cropped(16), h256::AlignRight), _v); }
bytes lookupAux(h256 _h) const { auto h = aux(_h); return m_aux.count(h) ? m_aux.at(h) : bytes(); }
void insertAux(h256 _h, bytesConstRef _v) { m_auxKey = aux(_h); m_aux[m_auxKey] = _v.toBytes(); }
std::set<h256> keys() const;
protected:
static h256 aux(h256 _k) { return h256(sha3(_k).ref().cropped(0, 24), h256::AlignLeft); }
std::map<h256, std::string> m_over;
std::map<h256, unsigned> m_refCount;
h256 m_auxKey;
std::map<h256, bytes> m_aux;
mutable bool m_enforceRefs = false;
};

13
libdevcrypto/OverlayDB.cpp

@ -19,6 +19,7 @@
* @date 2014
*/
#include <leveldb/db.h>
#include <libdevcore/Common.h>
#include "OverlayDB.h"
using namespace std;
@ -51,11 +52,23 @@ void OverlayDB::commit()
if (m_refCount[i.first])
m_db->Put(m_writeOptions, ldb::Slice((char const*)i.first.data(), i.first.size), ldb::Slice(i.second.data(), i.second.size()));
}
if (m_auxKey && m_aux.count(m_auxKey))
m_db->Put(m_writeOptions, m_auxKey.ref(), bytesConstRef(&m_aux[m_auxKey]));
m_over.clear();
m_refCount.clear();
}
}
bytes OverlayDB::lookupAux(h256 _h) const
{
bytes ret = MemoryDB::lookupAux(_h);
if (!ret.empty())
return ret;
std::string v;
m_db->Get(m_readOptions, _h.ref(), &v);
return asBytes(v);
}
void OverlayDB::rollback()
{
m_over.clear();

2
libdevcrypto/OverlayDB.h

@ -51,6 +51,8 @@ public:
bool exists(h256 _h) const;
void kill(h256 _h);
bytes lookupAux(h256 _h) const;
private:
using MemoryDB::clear;

2
libdevcrypto/TrieDB.h

@ -408,7 +408,7 @@ public:
Super::setRoot(h256(Super::db()->lookupAux(m_secure.root())));
}
h256 root() const { return m_secure.root(); }
h256 root() const { const_cast<FatGenericTrieDB*>(this)->syncRoot(); return m_secure.root(); }
void insert(bytesConstRef _key, bytesConstRef _value) { Super::insert(_key, _value); m_secure.insert(_key, _value); syncRoot(); }
void remove(bytesConstRef _key) { Super::remove(_key); m_secure.remove(_key); syncRoot(); }

2
libethcore/CommonEth.cpp

@ -33,7 +33,7 @@ namespace eth
{
const unsigned c_protocolVersion = 55;
const unsigned c_databaseVersion = 5 +
const unsigned c_databaseVersion = 6 +
#if ETH_FATDB
1000
#else

36
libethereum/Account.h

@ -76,28 +76,40 @@ public:
ContractConception
};
/// Changedness of account to create.
enum Changedness
{
/// Account starts as though it has been changed.
Changed,
/// Account starts as though it has not been changed.
Unchanged
};
/// Construct a dead Account.
Account() {}
/// Construct an alive Account, with given endowment, for either a normal (non-contract) account or for a
/// contract account in the
/// conception phase, where the code is not yet known.
Account(u256 _balance, NewAccountType _t): m_isAlive(true), m_balance(_balance), m_codeHash(_t == NormalCreation ? EmptySHA3 : c_contractConceptionCodeHash) {}
Account(u256 _balance, NewAccountType _t, Changedness _c = Changed): m_isAlive(true), m_isUnchanged(_c == Unchanged), m_balance(_balance), m_codeHash(_t == NormalCreation ? EmptySHA3 : c_contractConceptionCodeHash) {}
/// Explicit constructor for wierd cases of construction of a normal account.
Account(u256 _nonce, u256 _balance): m_isAlive(true), m_nonce(_nonce), m_balance(_balance) {}
Account(u256 _nonce, u256 _balance, Changedness _c = Changed): m_isAlive(true), m_isUnchanged(_c == Unchanged), m_nonce(_nonce), m_balance(_balance) {}
/// Explicit constructor for wierd cases of construction or a contract account.
Account(u256 _nonce, u256 _balance, h256 _contractRoot, h256 _codeHash): m_isAlive(true), m_nonce(_nonce), m_balance(_balance), m_storageRoot(_contractRoot), m_codeHash(_codeHash) { assert(_contractRoot); }
Account(u256 _nonce, u256 _balance, h256 _contractRoot, h256 _codeHash, Changedness _c): m_isAlive(true), m_isUnchanged(_c == Unchanged), m_nonce(_nonce), m_balance(_balance), m_storageRoot(_contractRoot), m_codeHash(_codeHash) { assert(_contractRoot); }
/// Kill this account. Useful for the suicide opcode. Following this call, isAlive() returns false.
void kill() { m_isAlive = false; m_storageOverlay.clear(); m_codeHash = EmptySHA3; m_storageRoot = EmptyTrie; m_balance = 0; m_nonce = 0; }
void kill() { m_isAlive = false; m_storageOverlay.clear(); m_codeHash = EmptySHA3; m_storageRoot = EmptyTrie; m_balance = 0; m_nonce = 0; changed(); }
/// @returns true iff this object represents an account in the state. Returns false if this object
/// represents an account that should no longer exist in the trie (an account that never existed or was
/// suicided).
bool isAlive() const { return m_isAlive; }
/// @returns true if the account is unchanged from creation.
bool isDirty() const { return !m_isUnchanged; }
/// @returns the balance of this account. Can be altered in place.
u256& balance() { return m_balance; }
@ -106,7 +118,7 @@ public:
u256 const& balance() const { return m_balance; }
/// Increments the balance of this account by the given amount. It's a bigint, so can be negative.
void addBalance(bigint _i) { m_balance = (u256)((bigint)m_balance + _i); }
void addBalance(bigint _i) { if (!_i) return; m_balance = (u256)((bigint)m_balance + _i); changed(); }
/// @returns the nonce of the account. Can be altered in place.
u256& nonce() { return m_nonce; }
@ -115,7 +127,7 @@ public:
u256 const& nonce() const { return m_nonce; }
/// Increment the nonce of the account by one.
void incNonce() { m_nonce++; }
void incNonce() { m_nonce++; changed(); }
/// @returns the root of the trie (whose nodes are stored in the state db externally to this class)
@ -127,7 +139,7 @@ public:
/// Set a key/value pair in the account's storage. This actually goes into the overlay, for committing
/// to the trie later.
void setStorage(u256 _p, u256 _v) { m_storageOverlay[_p] = _v; }
void setStorage(u256 _p, u256 _v) { m_storageOverlay[_p] = _v; changed(); }
/// @returns true if we are in the contract-conception state and setCode is valid to call.
bool isFreshCode() const { return m_codeHash == c_contractConceptionCodeHash; }
@ -140,8 +152,8 @@ public:
h256 codeHash() const { assert(!isFreshCode()); return m_codeHash; }
/// Sets the code of the account. Must only be called when isFreshCode() returns true.
void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = _code; }
void setCode(bytes const& _code) { assert(isFreshCode()); m_codeCache = _code; }
void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = _code; changed(); }
void setCode(bytes const& _code) { assert(isFreshCode()); m_codeCache = _code; changed(); }
/// @returns true if the account's code is available through code().
bool codeCacheValid() const { return m_codeHash == EmptySHA3 || m_codeHash == c_contractConceptionCodeHash || m_codeCache.size(); }
@ -154,9 +166,15 @@ public:
bytes const& code() const { assert(codeCacheValid()); return m_codeCache; }
private:
/// Note that we've altered the account.
void changed() { m_isUnchanged = false; }
/// Is this account existant? If not, it represents a deleted account.
bool m_isAlive = false;
/// True if we've not made any alteration to the account having been given it's properties directly.
bool m_isUnchanged = false;
/// Account's nonce.
u256 m_nonce = 0;

15
libethereum/BlockChain.cpp

@ -19,6 +19,8 @@
* @date 2014
*/
#include <leveldb/db.h>
#include "BlockChain.h"
#include <boost/filesystem.hpp>
@ -43,14 +45,17 @@ namespace js = json_spirit;
std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc)
{
string cmp = toBigEndianString(_bc.currentHash());
auto it = _bc.m_extrasDB->NewIterator(_bc.m_readOptions);
auto it = _bc.m_db->NewIterator(_bc.m_readOptions);
for (it->SeekToFirst(); it->Valid(); it->Next())
if (it->key().ToString() != "best")
{
string rlpString = it->value().ToString();
RLP r(rlpString);
BlockDetails d(r);
_out << toHex(it->key().ToString()) << ": " << d.number << " @ " << d.parent << (cmp == it->key().ToString() ? " BEST" : "") << std::endl;
try {
BlockInfo d(bytesConstRef(it->value()));
_out << toHex(it->key().ToString()) << ": " << d.number << " @ " << d.parentHash << (cmp == it->key().ToString() ? " BEST" : "") << std::endl;
}
catch (...) {
cwarn << "Invalid DB entry:" << toHex(it->key().ToString()) << " -> " << toHex(bytesConstRef(it->value()));
}
}
delete it;
return _out;

8
libethereum/State.cpp

@ -224,7 +224,7 @@ void State::ensureCached(std::map<Address, Account>& _cache, Address _a, bool _r
if (state.isNull())
s = Account(0, Account::NormalCreation);
else
s = Account(state[0].toInt<u256>(), state[1].toInt<u256>(), state[2].toHash<h256>(), state[3].toHash<h256>());
s = Account(state[0].toInt<u256>(), state[1].toInt<u256>(), state[2].toHash<h256>(), state[3].toHash<h256>(), Account::Unchanged);
bool ok;
tie(it, ok) = _cache.insert(make_pair(_a, s));
}
@ -625,7 +625,7 @@ void State::uncommitToMine()
if (!m_transactions.size())
m_state.setRoot(m_previousBlock.stateRoot);
else
m_state.setRoot(m_receipts[m_receipts.size() - 1].stateRoot());
m_state.setRoot(m_receipts.back().stateRoot());
m_db = m_lastTx;
paranoia("Uncommited to mine", true);
m_currentBlock.sha3Uncles = h256();
@ -890,7 +890,7 @@ Address State::newContract(u256 _balance, bytes const& _code)
auto it = m_cache.find(ret);
if (it == m_cache.end())
{
m_cache[ret] = Account(0, _balance, EmptyTrie, h);
m_cache[ret] = Account(0, _balance, EmptyTrie, h, Account::Changed);
return ret;
}
}
@ -1144,7 +1144,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s)
_out << "--- " << _s.rootHash() << std::endl;
std::set<Address> d;
std::set<Address> dtr;
auto trie = TrieDB<Address, OverlayDB>(const_cast<OverlayDB*>(&_s.m_db), _s.rootHash());
auto trie = SecureTrieDB<Address, OverlayDB>(const_cast<OverlayDB*>(&_s.m_db), _s.rootHash());
for (auto i: trie)
d.insert(i.first), dtr.insert(i.first);
for (auto i: _s.m_cache)

63
libethereum/State.h

@ -328,40 +328,43 @@ template <class DB>
void commit(std::map<Address, Account> const& _cache, DB& _db, SecureTrieDB<Address, DB>& _state)
{
for (auto const& i: _cache)
if (!i.second.isAlive())
_state.remove(i.first);
else
if (i.second.isDirty())
{
RLPStream s(4);
s << i.second.nonce() << i.second.balance();
if (i.second.storageOverlay().empty())
{
assert(i.second.baseRoot());
s.append(i.second.baseRoot());
}
if (!i.second.isAlive())
_state.remove(i.first);
else
{
SecureTrieDB<h256, DB> storageDB(&_db, i.second.baseRoot());
for (auto const& j: i.second.storageOverlay())
if (j.second)
storageDB.insert(j.first, rlp(j.second));
else
storageDB.remove(j.first);
assert(storageDB.root());
s.append(storageDB.root());
}
if (i.second.isFreshCode())
{
h256 ch = sha3(i.second.code());
_db.insert(ch, &i.second.code());
s << ch;
RLPStream s(4);
s << i.second.nonce() << i.second.balance();
if (i.second.storageOverlay().empty())
{
assert(i.second.baseRoot());
s.append(i.second.baseRoot());
}
else
{
SecureTrieDB<h256, DB> storageDB(&_db, i.second.baseRoot());
for (auto const& j: i.second.storageOverlay())
if (j.second)
storageDB.insert(j.first, rlp(j.second));
else
storageDB.remove(j.first);
assert(storageDB.root());
s.append(storageDB.root());
}
if (i.second.isFreshCode())
{
h256 ch = sha3(i.second.code());
_db.insert(ch, &i.second.code());
s << ch;
}
else
s << i.second.codeHash();
_state.insert(i.first, &s.out());
}
else
s << i.second.codeHash();
_state.insert(i.first, &s.out());
}
}

2
test/CMakeLists.txt

@ -26,7 +26,7 @@ target_link_libraries(testeth ethereum)
target_link_libraries(testeth ethcore)
target_link_libraries(testeth secp256k1)
target_link_libraries(testeth solidity)
if (NOT HEADLESS)
if (NOT HEADLESS AND NOT JUSTTESTS)
target_link_libraries(testeth webthree)
target_link_libraries(testeth natspec)
endif()

36
test/stateOriginal.cpp

@ -20,6 +20,7 @@
* State test functions.
*/
#include <boost/test/unit_test.hpp>
#include <boost/filesystem/operations.hpp>
#include <secp256k1/secp256k1.h>
#include <libethereum/CanonBlockChain.h>
@ -29,7 +30,21 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
int stateTest()
namespace dev
{
namespace test
{
int stateTest();
BOOST_AUTO_TEST_SUITE(StateIntegration)
BOOST_AUTO_TEST_CASE(Basic)
{
State s;
}
BOOST_AUTO_TEST_CASE(Complex)
{
cnote << "Testing State...";
@ -37,14 +52,15 @@ int stateTest()
KeyPair myMiner = sha3("Gav's Miner");
// KeyPair you = sha3("123");
Defaults::setDBPath(boost::filesystem::temp_directory_path().string());
Defaults::setDBPath(boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count()));
OverlayDB stateDB = State::openDB();
CanonBlockChain bc;
State s(myMiner.address(), stateDB);
cout << bc;
State s(myMiner.address(), stateDB);
cout << s;
// Sync up - this won't do much until we use the last state.
s.sync(bc);
@ -52,7 +68,7 @@ int stateTest()
// Mine to get some ether!
s.commitToMine(bc);
while (!s.mine(100).completed) {}
while (!s.mine(100, true).completed) {}
s.completeMine();
bc.attemptImport(s.blockData(), stateDB);
@ -65,7 +81,7 @@ int stateTest()
// Inject a transaction to transfer funds from miner to me.
bytes tx;
{
Transaction t(1000, 0, 0, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret());
Transaction t(1000, 10000, 10000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret());
assert(t.sender() == myMiner.address());
tx = t.rlp();
}
@ -76,7 +92,7 @@ int stateTest()
// Mine to get some ether and set in stone.
s.commitToMine(bc);
s.commitToMine(bc);
while (!s.mine(50).completed) { s.commitToMine(bc); }
while (!s.mine(100, true).completed) {}
s.completeMine();
bc.attemptImport(s.blockData(), stateDB);
@ -85,7 +101,9 @@ int stateTest()
s.sync(bc);
cout << s;
return 0;
}
BOOST_AUTO_TEST_SUITE_END()
}
}

20
test/trie.cpp

@ -52,6 +52,26 @@ using dev::operator <<;
BOOST_AUTO_TEST_SUITE(TrieTests)
BOOST_AUTO_TEST_CASE(fat_trie)
{
h256 r;
MemoryDB fm;
{
FatGenericTrieDB<MemoryDB> ft(&fm);
ft.init();
ft.insert(h256("69", h256::FromHex, h256::AlignRight).ref(), h256("414243", h256::FromHex, h256::AlignRight).ref());
for (auto i: ft)
cnote << i.first << i.second;
r = ft.root();
}
{
FatGenericTrieDB<MemoryDB> ft(&fm);
ft.setRoot(r);
for (auto i: ft)
cnote << i.first << i.second;
}
}
BOOST_AUTO_TEST_CASE(trie_test_anyorder)
{
string testPath = test::getTestPath();

Loading…
Cancel
Save