-
Notifications
You must be signed in to change notification settings - Fork 4
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
Statediff for full node #6
Changes from all commits
3a5f7b7
498831e
79686c9
08d0fe3
e380580
7995c5a
4d6ddf9
f2047cc
272d2f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,10 +71,11 @@ const ( | |
// CacheConfig contains the configuration values for the trie caching/pruning | ||
// that's resident in a blockchain. | ||
type CacheConfig struct { | ||
Disabled bool // Whether to disable trie write caching (archive node) | ||
TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory | ||
TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk | ||
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk | ||
Disabled bool // Whether to disable trie write caching (archive node) | ||
TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory | ||
TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk | ||
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk | ||
ProcessingStateDiffs bool // Whether statediffs processing should be taken into a account before a trie is pruned | ||
} | ||
|
||
// BlockChain represents the canonical chain given a database with a genesis | ||
|
@@ -136,6 +137,8 @@ type BlockChain struct { | |
|
||
badBlocks *lru.Cache // Bad block cache | ||
shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block. | ||
|
||
stateDiffsProcessed map[common.Hash]int | ||
} | ||
|
||
// NewBlockChain returns a fully initialised block chain using information | ||
|
@@ -155,24 +158,26 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par | |
blockCache, _ := lru.New(blockCacheLimit) | ||
futureBlocks, _ := lru.New(maxFutureBlocks) | ||
badBlocks, _ := lru.New(badBlockLimit) | ||
|
||
stateDiffsProcessed := make(map[common.Hash]int) | ||
bc := &BlockChain{ | ||
chainConfig: chainConfig, | ||
cacheConfig: cacheConfig, | ||
db: db, | ||
triegc: prque.New(nil), | ||
stateCache: state.NewDatabaseWithCache(db, cacheConfig.TrieCleanLimit), | ||
quit: make(chan struct{}), | ||
shouldPreserve: shouldPreserve, | ||
bodyCache: bodyCache, | ||
bodyRLPCache: bodyRLPCache, | ||
receiptsCache: receiptsCache, | ||
blockCache: blockCache, | ||
futureBlocks: futureBlocks, | ||
engine: engine, | ||
vmConfig: vmConfig, | ||
badBlocks: badBlocks, | ||
chainConfig: chainConfig, | ||
cacheConfig: cacheConfig, | ||
db: db, | ||
triegc: prque.New(nil), | ||
stateCache: state.NewDatabaseWithCache(db, cacheConfig.TrieCleanLimit), | ||
quit: make(chan struct{}), | ||
shouldPreserve: shouldPreserve, | ||
bodyCache: bodyCache, | ||
bodyRLPCache: bodyRLPCache, | ||
receiptsCache: receiptsCache, | ||
blockCache: blockCache, | ||
futureBlocks: futureBlocks, | ||
engine: engine, | ||
vmConfig: vmConfig, | ||
badBlocks: badBlocks, | ||
stateDiffsProcessed: stateDiffsProcessed, | ||
} | ||
|
||
bc.SetValidator(NewBlockValidator(chainConfig, bc, engine)) | ||
bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine)) | ||
|
||
|
@@ -922,6 +927,11 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e | |
return nil | ||
} | ||
|
||
func (bc *BlockChain) AddToStateDiffProcessedCollection(hash common.Hash) { | ||
count := bc.stateDiffsProcessed[hash] | ||
bc.stateDiffsProcessed[hash] = count + 1 | ||
} | ||
|
||
// WriteBlockWithState writes the block and all associated state to the database. | ||
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) { | ||
bc.wg.Add(1) | ||
|
@@ -994,6 +1004,16 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. | |
bc.triegc.Push(root, number) | ||
break | ||
} | ||
|
||
if bc.cacheConfig.ProcessingStateDiffs { | ||
if !bc.allowedRootToBeDereferenced(root.(common.Hash)) { | ||
bc.triegc.Push(root, number) | ||
break | ||
} else { | ||
delete(bc.stateDiffsProcessed, root.(common.Hash)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this mean we clean up the map when we're finished with a certain diff? Was just about to ask about memory leaks here, but probably okay if that's the case! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, if a root is okay to be dereferenced, we removed it from the stateDiffsProcessed collection just to make sure that that collection doesn't get huge. And then it will fall through to |
||
} | ||
} | ||
|
||
triedb.Dereference(root.(common.Hash)) | ||
} | ||
} | ||
|
@@ -1048,6 +1068,15 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. | |
return status, nil | ||
} | ||
|
||
// since we need the state tries of the current block and its parent in-memory | ||
// in order to process statediffs, we should avoid dereferencing roots until | ||
// its statediff and its child have been processed | ||
func (bc *BlockChain) allowedRootToBeDereferenced(root common.Hash) bool { | ||
diffProcessedForSelfAndChildCount := 2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feels like this constant could be ejected and set somewhere else so we don't have to assign it to a local closure all the time. |
||
count := bc.stateDiffsProcessed[root] | ||
return count >= diffProcessedForSelfAndChildCount | ||
} | ||
|
||
// addFutureBlock checks if the block is within the max allowed window to get | ||
// accepted for future processing, and returns an error if the block is too far | ||
// ahead and was not added. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1483,3 +1483,84 @@ func BenchmarkBlockChain_1x1000Executions(b *testing.B) { | |
|
||
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn) | ||
} | ||
|
||
func TestProcessingStateDiffs(t *testing.T) { | ||
defaultTrieCleanCache := 256 | ||
defaultTrieDirtyCache := 256 | ||
defaultTrieTimeout := 60 * time.Minute | ||
cacheConfig := &CacheConfig{ | ||
Disabled: false, | ||
TrieCleanLimit: defaultTrieCleanCache, | ||
TrieDirtyLimit: defaultTrieDirtyCache, | ||
TrieTimeLimit: defaultTrieTimeout, | ||
ProcessingStateDiffs: true, | ||
} | ||
db := ethdb.NewMemDatabase() | ||
genesis := new(Genesis).MustCommit(db) | ||
numberOfBlocks := triesInMemory | ||
engine := ethash.NewFaker() | ||
blockchain, _ := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil) | ||
blocks := makeBlockChain(genesis, numberOfBlocks+1, engine, db, canonicalSeed) | ||
_, err := blockchain.InsertChain(blocks) | ||
if err != nil { | ||
t.Fatalf("failed to create pristine chain: %v", err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. β¨ pristine β¨ |
||
} | ||
defer blockchain.Stop() | ||
|
||
//when adding a root hash to the collection, it will increment the count | ||
firstStateRoot := blocks[0].Root() | ||
blockchain.AddToStateDiffProcessedCollection(firstStateRoot) | ||
value, ok := blockchain.stateDiffsProcessed[firstStateRoot] | ||
if !ok { | ||
t.Error("state root not found in collection") | ||
} | ||
if value != 1 { | ||
t.Error("state root count not correct", "want", 1, "got", value) | ||
} | ||
|
||
blockchain.AddToStateDiffProcessedCollection(firstStateRoot) | ||
value, ok = blockchain.stateDiffsProcessed[firstStateRoot] | ||
if !ok { | ||
t.Error("state root not found in collection") | ||
} | ||
if value != 2 { | ||
t.Error("state root count not correct", "want", 2, "got", value) | ||
} | ||
|
||
moreBlocks := makeBlockChain(blocks[len(blocks)-1], 1, engine, db, canonicalSeed) | ||
_, err = blockchain.InsertChain(moreBlocks) | ||
|
||
//a root hash can be dereferenced when it's state diff and it's child's state diff have been processed | ||
//(i.e. it has a count of 2 in stateDiffsProcessed) | ||
nodes := blockchain.stateCache.TrieDB().Nodes() | ||
if containsRootHash(nodes, firstStateRoot) { | ||
t.Errorf("stateRoot %s in nodes, want: %t, got: %t", firstStateRoot.Hex(), false, true) | ||
} | ||
|
||
//a root hash should still be in the in-mem db if it's child's state diff hasn't yet been processed | ||
//(i.e. it has a count of 1 stateDiffsProcessed) | ||
secondStateRoot := blocks[1].Root() | ||
blockchain.AddToStateDiffProcessedCollection(secondStateRoot) | ||
if !containsRootHash(nodes, secondStateRoot) { | ||
t.Errorf("stateRoot %s in nodes, want: %t, got: %t", secondStateRoot.Hex(), true, false) | ||
} | ||
|
||
//the stateDiffsProcessed collection is cleaned up once a hash has been dereferenced | ||
_, ok = blockchain.stateDiffsProcessed[firstStateRoot] | ||
if ok { | ||
t.Errorf("stateRoot %s in stateDiffsProcessed collection, want: %t, got: %t", | ||
firstStateRoot.Hex(), | ||
false, | ||
ok, | ||
) | ||
} | ||
} | ||
|
||
func containsRootHash(collection []common.Hash, hash common.Hash) bool { | ||
for _, n := range collection { | ||
if n == hash { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This diff shows how we should write our tests instead of that damn Ginkgo! Looks so clean, and probably runs way faster. π Why are we even using Could probably be split up in smaller tests, but I figure the setup is a bit annoying. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,6 +59,8 @@ var DefaultConfig = Config{ | |
Blocks: 20, | ||
Percentile: 60, | ||
}, | ||
|
||
StateDiff: false, | ||
} | ||
|
||
func init() { | ||
|
@@ -135,6 +137,8 @@ type Config struct { | |
|
||
// Constantinople block override (TODO: remove after the fork) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π π΄ |
||
ConstantinopleOverride *big.Int | ||
|
||
StateDiff bool | ||
} | ||
|
||
type configMarshaling struct { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it make sense to combine this with the
RegisterStateDiffService
call, if both are based off of the call toctx.GlobalBool(utils.StateDiffFlag.Name
)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, good call!