Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core/state: access trie through Database interface, track errors #14589

Merged
merged 4 commits into from
Jun 27, 2017

Conversation

fjl
Copy link
Contributor

@fjl fjl commented Jun 6, 2017

Motivation for this change:

  • We can remove the light client's duplicated copy of core/state.
  • The light client now supports node iteration, so tracing and storage enumeration can
    work with the light client.

It opens the door to more code deletions later:

  • We can remove superfluous Trie methods Get, Update, ... in a later PR because they're
    no longer used by core/state.
  • We can remove SecureTrie because core/state is now aware of key hashing.
  • We can remove ForEachStorage because we now have a StorageTrie method.

trie/iterator.go Outdated
// Callers must not retain references to the return value after calling Next
Path() []byte

// Leaf returns true iff the current node is a leaf node.
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: "iff"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

see https://en.wiktionary.org/wiki/iff. I think it's not needed here, but the word was already there and I didn't want to change the original comment.

Copy link
Contributor

Choose a reason for hiding this comment

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

oh... got it, thanks

@fjl fjl force-pushed the state-errtrack branch from fc023e5 to a4f9acd Compare June 7, 2017 15:31
@@ -531,7 +526,7 @@ func (bc *BlockChain) HasBlockAndState(hash common.Hash) bool {
return false
}
// Ensure the associated state is also present
_, err := state.New(block.Root(), bc.chainDb)
_, err := state.New(block.Root(), bc.stateCache)
Copy link
Contributor

Choose a reason for hiding this comment

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

The state.New does a lot of allocation and things (https://github.com/ethereum/go-ethereum/blob/master/core/state/statedb.go#L89) . Seems a bit wasteful to instantiate one and just throw it away. Couldn't we just verify the state without actually allocating the entire instance in this case?

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 suppose we could use stateCache.OpenTrie to check.

}
state, err := state.New(parent.Root(), bc.stateCache)
Copy link
Contributor

Choose a reason for hiding this comment

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

For inserting N blocks, you'll be instantiating a new state N times. Is that really needed? In most situations (after the first block), the state will already be where you want it to be - no action needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The old version used Reset on stateCache, which was a StateDB. In the new code, stateCache is state.Database and state.New is about as expensive as Reset was.

@fjl fjl force-pushed the state-errtrack branch 3 times, most recently from ee25500 to 8a31557 Compare June 16, 2017 08:15
@fjl
Copy link
Contributor Author

fjl commented Jun 16, 2017

Note: the first few commits are part of #14615 and can be removed when that's merged.

@fjl fjl force-pushed the state-errtrack branch 2 times, most recently from 8e76ab5 to 1d90246 Compare June 20, 2017 16:32
@zsfelfoldi zsfelfoldi self-requested a review June 20, 2017 23:36
Copy link
Contributor

@zsfelfoldi zsfelfoldi left a comment

Choose a reason for hiding this comment

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

LGTM in general, see inline comments.

return &odrDatabase{ctx, StateTrieID(head), odr}
}

type odrDatabase struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we are implementing the entire odr database backend for the state here (not just the trie), maybe we should rename this file to something like odr_database.go.

func (m cachedTrie) CommitTo(dbw trie.DatabaseWriter) (common.Hash, error) {
root, err := m.SecureTrie.CommitTo(dbw)
if err == nil {
m.db.pushTrie(m.SecureTrie)
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe I'm mistaken but it seems like now we are caching storage tries too, which makes the entire pastTries cache useless since there are usually a lot of storage tries processed per block.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OpenStorageTrie doesn't return cachedTrie, it returns *trie.SecureTrie.

if codeHash == sha3_nil {
return nil, nil
}
if code, err := db.backend.Database().Get(codeHash[:]); err == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since StateDB is using a cachingDB which also checks the database, isn't this check redundant?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

StateDB doesn't use cachingDB is the Database returned by state.NewDatabase. It's not used here.

return len(code), err
}

type odrTrie struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is something I should have done already but please either add a comment stating that odrTrie is a SecureTrie (key caching is included) or rename it to odrSecureTrie. Maybe the comment is enough.

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 will move hashing to StateDB in a follow-up PR.

@fjl fjl added this to the 1.6.7 milestone Jun 21, 2017
@@ -0,0 +1,152 @@
// Copyright 2014 The go-ethereum Authors
Copy link
Contributor

Choose a reason for hiding this comment

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

s/2014/21017/

CommitTo(trie.DatabaseWriter) (common.Hash, error)
Hash() common.Hash
NodeIterator(startKey []byte) trie.NodeIterator
GetKey([]byte) []byte // TODO(fjl): remove this
Copy link
Contributor

Choose a reason for hiding this comment

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

When?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When SecureTrie is removed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a note to that effect? Otherwise there's no way to know when this todo is relevant for.

const (
// Number of past tries to keep. This value is chosen such that
// reasonable chain reorg depths will hit an existing trie.
maxPastTries = 12
Copy link
Contributor

Choose a reason for hiding this comment

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

Mmm, pastries.

type Database interface {
// Accessing tries
OpenTrie(root common.Hash) (Trie, error)
OpenStorageTrie(addrHash, root common.Hash) (Trie, error)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why can't we just use OpenTrie here? And what is addrHash used for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

IMHO it's useful to distinguish between storage tries and the main accounts trie. For example, the cachingDB caches the accounts trie on commit, but doesn't cache storage at all. addrHash is used by the light client, you can see how in les/trie.go.

@fjl fjl force-pushed the state-errtrack branch from e0683a8 to 916ee18 Compare June 27, 2017 10:47
@fjl fjl force-pushed the state-errtrack branch from f364f52 to c5a5bf7 Compare June 27, 2017 12:00
@fjl fjl merged commit 9e5f03b into ethereum:master Jun 27, 2017
func (t *odrTrie) TryUpdate(key, value []byte) error {
key = crypto.Keccak256(key)
return t.do(key, func() error {
return t.trie.TryDelete(key)
Copy link
Contributor

@SheldonZhong SheldonZhong Nov 8, 2018

Choose a reason for hiding this comment

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

I have written my question in here. I'm not sure whether anybody would be notified to see it. So I comment again in this PR. @fjl

Hi there, I'm confused here a little bit. Why TryUpdate does not call t.trie.TryUpdate(key, value) and calls t.trie.TryDelete instead? This is not apparent from my point of view. The update operation simply deletes the corresponding entry, though it could retrieve later by odr. However, it adds further network overhead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants