Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

debug_accountRangeAt #4987

Merged
merged 5 commits into from
May 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libdevcore/TrieDB.h
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ class FatGenericTrieDB: private SpecificTrieDB<GenericTrieDB<_DB>, h256>

HashedIterator() {}
HashedIterator(FatGenericTrieDB const* _trie) : Super(_trie) {}
HashedIterator(FatGenericTrieDB const* _trie, bytesConstRef _hashedKey): Super(_trie, _hashedKey) {}

bytes key() const
{
Expand All @@ -490,6 +491,7 @@ class FatGenericTrieDB: private SpecificTrieDB<GenericTrieDB<_DB>, h256>

HashedIterator hashedBegin() const { return HashedIterator(this); }
HashedIterator hashedEnd() const { return HashedIterator(); }
HashedIterator hashedLowerBound(h256 const& _hashedKey) const { return HashedIterator(this, _hashedKey.ref()); }
};

template <class KeyType, class DB> using TrieDB = SpecificTrieDB<GenericTrieDB<DB>, KeyType>;
Expand Down
53 changes: 53 additions & 0 deletions libethereum/State.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,59 @@ unordered_map<Address, u256> State::addresses() const
#endif
}

std::pair<State::AddressMap, h256> State::addresses(
h256 const& _beginHash, size_t _maxResults) const
{
AddressMap addresses;
h256 nextKey;

for (auto it = m_state.hashedLowerBound(_beginHash); it != m_state.hashedEnd(); ++it)
{
auto const address = Address(it.key());
auto const itCachedAddress = m_cache.find(address);

// skip if deleted in cache
if (itCachedAddress != m_cache.end() && itCachedAddress->second.isDirty() &&
!itCachedAddress->second.isAlive())
continue;

// break when _maxResults fetched
if (addresses.size() == _maxResults)
{
nextKey = h256((*it).first);
break;
}

h256 const hashedAddress((*it).first);
addresses[hashedAddress] = address;
}

// get addresses from cache with hash >= _beginHash (both new and old touched, we can't
// distinguish them) and order by hash
AddressMap cacheAddresses;
for (auto const& addressAndAccount : m_cache)
{
auto const& address = addressAndAccount.first;
auto const addressHash = sha3(address);
auto const& account = addressAndAccount.second;
if (account.isDirty() && account.isAlive() && addressHash >= _beginHash)
cacheAddresses.emplace(addressHash, address);
}

// merge addresses from DB and addresses from cache
addresses.insert(cacheAddresses.begin(), cacheAddresses.end());

// if some new accounts were created in cache we need to return fewer results
if (addresses.size() > _maxResults)
{
auto itEnd = std::next(addresses.begin(), _maxResults);
nextKey = itEnd->first;
addresses.erase(itEnd, addresses.end());
}

return {addresses, nextKey};
}

void State::setRoot(h256 const& _r)
{
m_cache.clear();
Expand Down
6 changes: 6 additions & 0 deletions libethereum/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ class State
RemoveEmptyAccounts
};

using AddressMap = std::map<h256, Address>;

/// Default constructor; creates with a blank database prepopulated with the genesis block.
explicit State(u256 const& _accountStartNonce): State(_accountStartNonce, OverlayDB(), BaseState::Empty) {}

Expand Down Expand Up @@ -207,6 +209,10 @@ class State
/// @throws InterfaceNotSupported if compiled without ETH_FATDB.
std::unordered_map<Address, u256> addresses() const;

/// @returns the map with maximum _maxResults elements containing hash->addresses and the next
/// address hash. This method faster then addresses() const;
std::pair<AddressMap, h256> addresses(h256 const& _begin, size_t _maxResults) const;

/// Execute a given transaction.
/// This will change the state accordingly.
std::pair<ExecutionResult, TransactionReceipt> execute(EnvInfo const& _envInfo, SealEngineFace const& _sealEngine, Transaction const& _t, Permanence _p = Permanence::Committed, OnOpFunc const& _onOp = OnOpFunc());
Expand Down
35 changes: 35 additions & 0 deletions libweb3jsonrpc/Debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,41 @@ Json::Value Debug::debug_traceBlockByNumber(int _blockNumber, Json::Value const&
return ret;
}

Json::Value Debug::debug_accountRangeAt(
string const& _blockHashOrNumber, int _txIndex, string const& _addressHash, int _maxResults)
{
Json::Value ret(Json::objectValue);

if (_txIndex < 0)
throw jsonrpc::JsonRpcException("Negative index");
if (_maxResults <= 0)
throw jsonrpc::JsonRpcException("Nonpositive maxResults");

try
{
Block block = m_eth.block(blockHash(_blockHashOrNumber));
size_t const i = std::min(static_cast<size_t>(_txIndex), block.pending().size());
State state(State::Null);
createIntermediateState(state, block, i, m_eth.blockChain());

auto const addressMap = state.addresses(h256(_addressHash), _maxResults);

Json::Value addressList(Json::objectValue);
for (auto const& record : addressMap.first)
addressList[toString(record.first)] = toString(record.second);

ret["addressMap"] = addressList;
ret["nextKey"] = toString(addressMap.second);
}
catch (Exception const& _e)
{
cwarn << diagnostic_information(_e);
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_RPC_INVALID_PARAMS);
}

return ret;
}

Json::Value Debug::debug_storageRangeAt(string const& _blockHashOrNumber, int _txIndex, string const& _address, string const& _begin, int _maxResults)
{
Json::Value ret(Json::objectValue);
Expand Down
5 changes: 3 additions & 2 deletions libweb3jsonrpc/Debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ class Debug: public DebugFace
return RPCModules{RPCModule{"debug", "1.0"}};
}


virtual Json::Value debug_traceTransaction(std::string const& _txHash, Json::Value const& _json) override;
virtual Json::Value debug_accountRangeAt(std::string const& _blockHashOrNumber, int _txIndex,
std::string const& _addressHash, int _maxResults) override;
virtual Json::Value debug_traceTransaction(std::string const& _txHash, Json::Value const& _json) override;
virtual Json::Value debug_traceCall(Json::Value const& _call, std::string const& _blockNumber, Json::Value const& _options) override;
virtual Json::Value debug_traceBlockByNumber(int _blockNumber, Json::Value const& _json) override;
virtual Json::Value debug_traceBlockByHash(std::string const& _blockHash, Json::Value const& _json) override;
Expand Down
7 changes: 6 additions & 1 deletion libweb3jsonrpc/DebugFace.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@ namespace dev {
public:
DebugFace()
{
this->bindAndAddMethod(jsonrpc::Procedure("debug_accountRangeAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER,"param3",jsonrpc::JSON_STRING,"param4",jsonrpc::JSON_INTEGER, NULL), &dev::rpc::DebugFace::debug_accountRangeAtI);
this->bindAndAddMethod(jsonrpc::Procedure("debug_traceTransaction", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_OBJECT, NULL), &dev::rpc::DebugFace::debug_traceTransactionI);
this->bindAndAddMethod(jsonrpc::Procedure("debug_storageRangeAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER,"param3",jsonrpc::JSON_STRING,"param4",jsonrpc::JSON_STRING,"param5",jsonrpc::JSON_INTEGER, NULL), &dev::rpc::DebugFace::debug_storageRangeAtI);
this->bindAndAddMethod(jsonrpc::Procedure("debug_preimage", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &dev::rpc::DebugFace::debug_preimageI);
this->bindAndAddMethod(jsonrpc::Procedure("debug_traceBlockByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_OBJECT, NULL), &dev::rpc::DebugFace::debug_traceBlockByNumberI);
this->bindAndAddMethod(jsonrpc::Procedure("debug_traceBlockByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_OBJECT, NULL), &dev::rpc::DebugFace::debug_traceBlockByHashI);
this->bindAndAddMethod(jsonrpc::Procedure("debug_traceCall", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_OBJECT,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_OBJECT, NULL), &dev::rpc::DebugFace::debug_traceCallI);
}

inline virtual void debug_accountRangeAtI(const Json::Value &request, Json::Value &response)
{
response = this->debug_accountRangeAt(request[0u].asString(), request[1u].asInt(), request[2u].asString(), request[3u].asInt());
}
inline virtual void debug_traceTransactionI(const Json::Value &request, Json::Value &response)
{
response = this->debug_traceTransaction(request[0u].asString(), request[1u]);
Expand All @@ -46,6 +50,7 @@ namespace dev {
{
response = this->debug_traceCall(request[0u], request[1u].asString(), request[2u]);
}
virtual Json::Value debug_accountRangeAt(const std::string& param1, int param2, const std::string& param3, int param4) = 0;
virtual Json::Value debug_traceTransaction(const std::string& param1, const Json::Value& param2) = 0;
virtual Json::Value debug_storageRangeAt(const std::string& param1, int param2, const std::string& param3, const std::string& param4, int param5) = 0;
virtual std::string debug_preimage(const std::string& param1) = 0;
Expand Down
1 change: 1 addition & 0 deletions libweb3jsonrpc/debug.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[
{ "name": "debug_accountRangeAt", "params": ["", 0, "", 0], "returns": {}},
{ "name": "debug_traceTransaction", "params": ["", {}], "returns": {}},
{ "name": "debug_storageRangeAt", "params": ["", 0, "", "", 0], "returns": {}},
{ "name": "debug_preimage", "params": [""], "returns": ""},
Expand Down
37 changes: 37 additions & 0 deletions test/unittests/libdevcrypto/trie.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,43 @@ BOOST_AUTO_TEST_CASE(trieLowerBound)
}
}

BOOST_AUTO_TEST_CASE(hashedLowerBound)
{
// get some random keys and their hashes
std::map<h256, std::string> hashToKey;
for (int i = 0; i < 10; ++i)
{
std::string key = toString(i);
hashToKey[sha3(key)] = key;
}

// insert keys into trie
MemoryDB memdb;
FatGenericTrieDB<MemoryDB> trie(&memdb);
trie.init();

for (auto const& hashAndKey : hashToKey)
trie.insert(hashAndKey.second, std::string("value doesn't matter"));


// Trie should have the same order of hashed keys as the map.
// Get some random hashed key to start iteration
auto itHashToKey = hashToKey.begin();
++itHashToKey;
++itHashToKey;

// check trie iteration against map iteration
for (auto itTrie = trie.hashedLowerBound(itHashToKey->first); itTrie != trie.hashedEnd(); ++itTrie, ++itHashToKey)
{
// check hashed key
BOOST_CHECK((*itTrie).first.toBytes() == itHashToKey->first.asBytes());
// check key
BOOST_CHECK(itTrie.key() == bytes(itHashToKey->second.begin(), itHashToKey->second.end()));
}

BOOST_CHECK(itHashToKey == hashToKey.end());
}

BOOST_AUTO_TEST_CASE(trieStess)
{
cnote << "Stress-testing Trie...";
Expand Down
142 changes: 120 additions & 22 deletions test/unittests/libethereum/StateUnitTests.cpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
/*
This file is part of cpp-ethereum.
This file is part of cpp-ethereum.

cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/// @file
/// State unit tests.
Expand All @@ -36,24 +36,122 @@ BOOST_FIXTURE_TEST_SUITE(StateUnitTests, TestOutputHelperFixture)

BOOST_AUTO_TEST_CASE(Basic)
{
Block s(Block::Null);
Block s(Block::Null);
}

BOOST_AUTO_TEST_CASE(LoadAccountCode)
{
Address addr{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"};
State s{0};
s.createContract(addr);
uint8_t codeData[] = {'c', 'o', 'd', 'e'};
s.setCode(addr, {std::begin(codeData), std::end(codeData)});
s.commit(State::CommitBehaviour::RemoveEmptyAccounts);
Address addr{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"};
State s{0};
s.createContract(addr);
uint8_t codeData[] = {'c', 'o', 'd', 'e'};
s.setCode(addr, {std::begin(codeData), std::end(codeData)});
s.commit(State::CommitBehaviour::RemoveEmptyAccounts);

auto& loadedCode = s.code(addr);
BOOST_CHECK(std::equal(
std::begin(codeData), std::end(codeData), std::begin(loadedCode)
));
auto& loadedCode = s.code(addr);
BOOST_CHECK(std::equal(
std::begin(codeData), std::end(codeData), std::begin(loadedCode)
));
}

class AddressRangeTestFixture : public TestOutputHelperFixture
{
public:
AddressRangeTestFixture()
{
// get some random addresses and their hashes
for (unsigned i = 0; i < addressCount; ++i)
{
Address addr{i};
hashToAddress[sha3(addr)] = addr;
}

// create accounts in the state
for (auto const& hashAndAddr : hashToAddress)
state.addBalance(hashAndAddr.second, 100);
state.commit(State::CommitBehaviour::RemoveEmptyAccounts);
}

State state{0};
unsigned const addressCount = 10;
std::map<h256, Address> hashToAddress;
};

BOOST_FIXTURE_TEST_SUITE(StateAddressRangeTests, AddressRangeTestFixture)


BOOST_AUTO_TEST_CASE(addressesReturnsAllAddresses)
{
std::pair<State::AddressMap, h256> addressesAndNextKey =
state.addresses(h256{}, addressCount * 2);
State::AddressMap addresses = addressesAndNextKey.first;

BOOST_CHECK_EQUAL(addresses.size(), addressCount);
BOOST_CHECK(addresses == hashToAddress);
BOOST_CHECK_EQUAL(addressesAndNextKey.second, h256{});
}

BOOST_AUTO_TEST_CASE(addressesReturnsNoMoreThanRequested)
{
int maxResults = 3;
std::pair<State::AddressMap, h256> addressesAndNextKey = state.addresses(h256{}, maxResults);
State::AddressMap& addresses = addressesAndNextKey.first;
h256& nextKey = addressesAndNextKey.second;

BOOST_CHECK_EQUAL(addresses.size(), maxResults);
auto itHashToAddressEnd = std::next(hashToAddress.begin(), maxResults);
BOOST_CHECK(addresses == State::AddressMap(hashToAddress.begin(), itHashToAddressEnd));
BOOST_CHECK_EQUAL(nextKey, itHashToAddressEnd->first);

// request next chunk
std::pair<State::AddressMap, h256> addressesAndNextKey2 = state.addresses(nextKey, maxResults);
State::AddressMap& addresses2 = addressesAndNextKey2.first;
BOOST_CHECK_EQUAL(addresses2.size(), maxResults);
auto itHashToAddressEnd2 = std::next(itHashToAddressEnd, maxResults);
BOOST_CHECK(addresses2 == State::AddressMap(itHashToAddressEnd, itHashToAddressEnd2));
}

BOOST_AUTO_TEST_CASE(addressesDoesntReturnDeletedInCache)
{
// delete some accounts
unsigned deleteCount = 3;
auto it = hashToAddress.begin();
for (unsigned i = 0; i < deleteCount; ++i, ++it)
state.kill(it->second);
// don't commmit

std::pair<State::AddressMap, h256> addressesAndNextKey =
state.addresses(h256{}, addressCount * 2);
State::AddressMap& addresses = addressesAndNextKey.first;
BOOST_CHECK_EQUAL(addresses.size(), addressCount - deleteCount);
BOOST_CHECK(addresses == State::AddressMap(it, hashToAddress.end()));
}

BOOST_AUTO_TEST_CASE(addressesReturnsCreatedInCache)
{
// create some accounts
unsigned createCount = 3;
std::map<h256, Address> newHashToAddress;
for (unsigned i = addressCount; i < addressCount + createCount; ++i)
{
Address addr{i};
newHashToAddress[sha3(addr)] = addr;
}

// create accounts in the state
for (auto const& hashAndAddr : newHashToAddress)
state.addBalance(hashAndAddr.second, 100);
// don't commmit

std::pair<State::AddressMap, h256> addressesAndNextKey =
state.addresses(newHashToAddress.begin()->first, addressCount + createCount);
State::AddressMap& addresses = addressesAndNextKey.first;
for (auto const& hashAndAddr : newHashToAddress)
BOOST_CHECK(addresses.find(hashAndAddr.first) != addresses.end());
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()

}
Expand Down