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

debug_accountRangeAt #4987

merged 5 commits into from
May 24, 2018

Conversation

winsvega
Copy link
Contributor

@winsvega winsvega commented Apr 27, 2018

Debug account range at.

Get the list of accounts in the given block after given transaction index with provided account mask.

ethereum/retesteth#18

@winsvega winsvega requested review from pirapira and gumb0 April 27, 2018 21:02
{
Block block = m_eth.block(blockHash(_blockHashOrNumber));

unsigned const i = ((unsigned)_txIndex < block.pending().size()) ? (unsigned)_txIndex : block.pending().size();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use static_cast for casting and do it once.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check Debug::debug_storageRangeAt below this method

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy-pasting bad code is still bad code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean should I fix that part too I suppose I should.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can but that's not required.
Here I'd do:

size_t const i = std::min(static_cast<size_t>(_txIndex), block.pending().size());

But I'm not sure this is good logic. Shouldn't we report error in case the transaction index is wrong? And txIndex == block.pending().size() seems to be also wrong.

unsigned const i = ((unsigned)_txIndex < block.pending().size()) ? (unsigned)_txIndex : block.pending().size();
State state(State::Null);
createIntermediateState(state, block, i, m_eth.blockChain());
Address from(_address);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you use const for local variables, do it everywhere.

State state(State::Null);
createIntermediateState(state, block, i, m_eth.blockChain());
Address from(_address);
for (auto const& addr : state.addresses())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not seem to be efficient way of filtering accounts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so the better way I see would be to sort addresses first then just select address

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not at all. The problem is you have to load all accounts from database first. That's not a big deal in test chain but on the mainnet that's not feasible. You have to extend State interface to support returning a subset of addresses.

Copy link
Member

@gumb0 gumb0 Apr 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also it should return hashes of addresses, not addresses, because addresses are not always available (e.g. not available after snapshot sync)

@cdetrio also mentioned it in ethereum/retesteth#5 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm. not sure what I would do with address hashes. if this function return list of hashes I wont be able to tell which address is unexpectedly exist in the post state.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional is fine. I don't get how @chfast wants to optimize and select required addresses from m_state without iterating. is m_state ordered?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure it's ordered, it's a trie. I think you should utilize lower_bound method of the Trie classes. Somewhat similar to how debug_storageRangeAt finds the point to start iterating.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gumb0 The key in the database is the hash of the address? so we cannot filter the addresses without first loading it into the cache?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chfast I think we can, we'll just have a special method for this in State, that will not update m_cache, but use lower-level feature of TrieDB

It's similar to State::storage(Address const& _id) (which is used for debug_storageRangeAt) - it iterates over the storage directly from the database, storage cache is not updated

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my intention here from the beginning.

@codecov-io
Copy link

codecov-io commented Apr 27, 2018

Codecov Report

Merging #4987 into develop will increase coverage by 0.02%.
The diff coverage is 78.12%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #4987      +/-   ##
===========================================
+ Coverage    59.74%   59.76%   +0.02%     
===========================================
  Files          346      346              
  Lines        26685    26870     +185     
  Branches      3188     3222      +34     
===========================================
+ Hits         15943    16060     +117     
- Misses        9692     9738      +46     
- Partials      1050     1072      +22

@gumb0
Copy link
Member

gumb0 commented May 8, 2018

Now you should be able to define a method in State something like

pair<map<h256, Address>,h256> State::addresses(h256 const& _begin, int _maxResults) const
{
    map<h256, Address> ret;
    h256 nextKey;


    for (auto it = m_state.hashedLowerBound(_begin); it != m_state.hashedEnd(); ++it)
    {
        if (ret.size() == _maxResults)
        {
           nextKey = (*it).first;
           break;
        }

        h256 const hashedAddress((*it).first);
        Address const address = Address(it.key());
        // maybe here check in m_cache whether account is still alive
        ret[hashedAddress] = address;
    }

    return {ret, nextKey};
}

@gumb0
Copy link
Member

gumb0 commented May 18, 2018

The problem with my code there ^ discovered.
We should take into account m_cache better, that is some account could be created but exist only in m_cache, not in m_state. We should merge them somehow, but m_cache is ordered by Address while m_state by hash of address...

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);
if (block.info().number() >= m_eth.chainParams().byzantiumForkBlock)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be the other way around? We have to create intermediate state in Byzantium+, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createIntermediateState works for both cases (creates state from intermediate root when available, executes previous transactions when not)
Then State::addresses() doesn't work right for this state with re-executed transactions (the result of execution is in cache and it doesn't take into account cache)

This if shouldn't be here and State::addresses() should be fixed, I'm going to do it

@@ -207,6 +207,11 @@ 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;
typedef std::map<h256, Address> addressMap;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@@ -234,6 +234,28 @@ unordered_map<Address, u256> State::addresses() const
#endif
}

std::pair<State::addressMap, h256> State::addresses(h256 const& _begin, size_t _maxResults) const
{
map<h256, Address> ret;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use AddressMap type.

gumb0
gumb0 previously requested changes May 22, 2018
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);
if (block.info().number() >= m_eth.chainParams().byzantiumForkBlock)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createIntermediateState works for both cases (creates state from intermediate root when available, executes previous transactions when not)
Then State::addresses() doesn't work right for this state with re-executed transactions (the result of execution is in cache and it doesn't take into account cache)

This if shouldn't be here and State::addresses() should be fixed, I'm going to do it

@gumb0 gumb0 dismissed their stale review May 22, 2018 15:20

@winsvega Please check how it now works for you

@winsvega
Copy link
Contributor Author

winsvega commented May 23, 2018

works

*** No errors detected
Total Time:                                       : 490               
stStaticCall                                  time: 85.592            
stTransactionTest                             time: 60.4297           
stPreCompiledContracts                        time: 59.8086           
stZeroKnowledge                               time: 57.9728           
stBadOpcode                                   time: 53.135            
stStackTests                                  time: 20.6821           
stRevertTest                                  time: 20.5036           
stRandom                                      time: 15.6341           
stCallCodes                                   time: 13.4573           
stPreCompiledContracts2                       time: 12.5582           
stRandom2                                     time: 10.5036           
stZeroKnowledge2                              time: 9.31869 

I still think there should be a way for optimization of eth calls.
and cpp client in general.

A simple transaction import runs the VM execution twice.

  1. upon transaction import (send raw transaction)
  2. when mining a block.

@chfast
Copy link
Member

chfast commented May 23, 2018

This is ok, but AppVeyor fails to test within 1 hour.

@chfast
Copy link
Member

chfast commented May 23, 2018

Could the performance degraded because of the changes? AppVeyor is on the edge now.

@winsvega
Copy link
Contributor Author

unit test could have added some to execution time I think.

@gumb0
Copy link
Member

gumb0 commented May 24, 2018

@chfast No, new unit tests are quick I think. There's one of the old tests that hangs sometimes #5038 I'll fix it

@gumb0 gumb0 merged commit aacde6e into develop May 24, 2018
@gumb0 gumb0 deleted the smallrpc2 branch May 24, 2018 17:38
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants