make proofs and paths optional + compress service loop into single for loop (may be missing something here)
This commit is contained in:
parent
71b41b5c77
commit
5c62d0bcb1
@ -147,6 +147,8 @@ var (
|
|||||||
utils.EWASMInterpreterFlag,
|
utils.EWASMInterpreterFlag,
|
||||||
utils.EVMInterpreterFlag,
|
utils.EVMInterpreterFlag,
|
||||||
utils.StateDiffFlag,
|
utils.StateDiffFlag,
|
||||||
|
utils.StateDiffPathsAndProofs,
|
||||||
|
utils.StateDiffLeafNodesOnly,
|
||||||
configFileFlag,
|
configFileFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,6 +255,8 @@ var AppHelpFlagGroups = []flagGroup{
|
|||||||
Name: "STATE DIFF",
|
Name: "STATE DIFF",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
utils.StateDiffFlag,
|
utils.StateDiffFlag,
|
||||||
|
utils.StateDiffPathsAndProofs,
|
||||||
|
utils.StateDiffLeafNodesOnly,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -761,6 +761,14 @@ var (
|
|||||||
Name: "statediff",
|
Name: "statediff",
|
||||||
Usage: "Enables the calculation of state diffs between each block, persists these state diffs the configured persistence mode.",
|
Usage: "Enables the calculation of state diffs between each block, persists these state diffs the configured persistence mode.",
|
||||||
}
|
}
|
||||||
|
StateDiffPathsAndProofs = cli.BoolFlag{
|
||||||
|
Name: "statediff.pathsandproofs",
|
||||||
|
Usage: "Path and proof sets for the state and storage nodes are generated",
|
||||||
|
}
|
||||||
|
StateDiffLeafNodesOnly = cli.BoolFlag{
|
||||||
|
Name: "statediff.leafs",
|
||||||
|
Usage: "Consider only leaf nodes of the storage and state tries",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// MakeDataDir retrieves the currently requested data directory, terminating
|
// MakeDataDir retrieves the currently requested data directory, terminating
|
||||||
@ -1634,12 +1642,16 @@ func RegisterGraphQLService(stack *node.Node, endpoint string, cors, vhosts []st
|
|||||||
|
|
||||||
// RegisterStateDiffService configures and registers a service to stream state diff data over RPC
|
// RegisterStateDiffService configures and registers a service to stream state diff data over RPC
|
||||||
func RegisterStateDiffService(stack *node.Node, ctx *cli.Context) {
|
func RegisterStateDiffService(stack *node.Node, ctx *cli.Context) {
|
||||||
|
config := statediff.Config{
|
||||||
|
PathsAndProofs: ctx.GlobalBool(StateDiffPathsAndProofs.Name),
|
||||||
|
LeafsOnly: ctx.GlobalBool(StateDiffLeafNodesOnly.Name),
|
||||||
|
}
|
||||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
var ethServ *eth.Ethereum
|
var ethServ *eth.Ethereum
|
||||||
ctx.Service(ðServ)
|
ctx.Service(ðServ)
|
||||||
chainDb := ethServ.ChainDb()
|
chainDb := ethServ.ChainDb()
|
||||||
blockChain := ethServ.BlockChain()
|
blockChain := ethServ.BlockChain()
|
||||||
return statediff.NewStateDiffService(chainDb, blockChain)
|
return statediff.NewStateDiffService(chainDb, blockChain, config)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
Fatalf("Failed to register State Diff Service", err)
|
Fatalf("Failed to register State Diff Service", err)
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
package statediff
|
package statediff
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
@ -37,13 +38,15 @@ type Builder interface {
|
|||||||
|
|
||||||
type builder struct {
|
type builder struct {
|
||||||
chainDB ethdb.Database
|
chainDB ethdb.Database
|
||||||
|
config Config
|
||||||
blockChain *core.BlockChain
|
blockChain *core.BlockChain
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBuilder is used to create a builder
|
// NewBuilder is used to create a state diff builder
|
||||||
func NewBuilder(db ethdb.Database, blockChain *core.BlockChain) Builder {
|
func NewBuilder(db ethdb.Database, blockChain *core.BlockChain, config Config) Builder {
|
||||||
return &builder{
|
return &builder{
|
||||||
chainDB: db,
|
chainDB: db,
|
||||||
|
config: config,
|
||||||
blockChain: blockChain,
|
blockChain: blockChain,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,13 +57,11 @@ func (sdb *builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, block
|
|||||||
stateCache := sdb.blockChain.StateCache()
|
stateCache := sdb.blockChain.StateCache()
|
||||||
oldTrie, err := stateCache.OpenTrie(oldStateRoot)
|
oldTrie, err := stateCache.OpenTrie(oldStateRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error creating trie for oldStateRoot", "error", err)
|
return StateDiff{}, fmt.Errorf("error creating trie for oldStateRoot: %v", err)
|
||||||
return StateDiff{}, err
|
|
||||||
}
|
}
|
||||||
newTrie, err := stateCache.OpenTrie(newStateRoot)
|
newTrie, err := stateCache.OpenTrie(newStateRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error creating trie for newStateRoot", "error", err)
|
return StateDiff{}, fmt.Errorf("error creating trie for newStateRoot: %v", err)
|
||||||
return StateDiff{}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find created accounts
|
// Find created accounts
|
||||||
@ -68,8 +69,7 @@ func (sdb *builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, block
|
|||||||
newIt := newTrie.NodeIterator([]byte{})
|
newIt := newTrie.NodeIterator([]byte{})
|
||||||
creations, err := sdb.collectDiffNodes(oldIt, newIt)
|
creations, err := sdb.collectDiffNodes(oldIt, newIt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error collecting creation diff nodes", "error", err)
|
return StateDiff{}, fmt.Errorf("error collecting creation diff nodes: %v", err)
|
||||||
return StateDiff{}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find deleted accounts
|
// Find deleted accounts
|
||||||
@ -77,8 +77,7 @@ func (sdb *builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, block
|
|||||||
newIt = newTrie.NodeIterator([]byte{})
|
newIt = newTrie.NodeIterator([]byte{})
|
||||||
deletions, err := sdb.collectDiffNodes(newIt, oldIt)
|
deletions, err := sdb.collectDiffNodes(newIt, oldIt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error collecting deletion diff nodes", "error", err)
|
return StateDiff{}, fmt.Errorf("error collecting deletion diff nodes: %v", err)
|
||||||
return StateDiff{}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find all the diffed keys
|
// Find all the diffed keys
|
||||||
@ -89,18 +88,15 @@ func (sdb *builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, block
|
|||||||
// Build and return the statediff
|
// Build and return the statediff
|
||||||
updatedAccounts, err := sdb.buildDiffIncremental(creations, deletions, updatedKeys)
|
updatedAccounts, err := sdb.buildDiffIncremental(creations, deletions, updatedKeys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error building diff for updated accounts", "error", err)
|
return StateDiff{}, fmt.Errorf("error building diff for updated accounts: %v", err)
|
||||||
return StateDiff{}, err
|
|
||||||
}
|
}
|
||||||
createdAccounts, err := sdb.buildDiffEventual(creations)
|
createdAccounts, err := sdb.buildDiffEventual(creations)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error building diff for created accounts", "error", err)
|
return StateDiff{}, fmt.Errorf("error building diff for created accounts: %v", err)
|
||||||
return StateDiff{}, err
|
|
||||||
}
|
}
|
||||||
deletedAccounts, err := sdb.buildDiffEventual(deletions)
|
deletedAccounts, err := sdb.buildDiffEventual(deletions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error building diff for deleted accounts", "error", err)
|
return StateDiff{}, fmt.Errorf("error building diff for deleted accounts: %v", err)
|
||||||
return StateDiff{}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return StateDiff{
|
return StateDiff{
|
||||||
@ -116,38 +112,69 @@ func (sdb *builder) collectDiffNodes(a, b trie.NodeIterator) (AccountsMap, error
|
|||||||
var diffAccounts = make(AccountsMap)
|
var diffAccounts = make(AccountsMap)
|
||||||
it, _ := trie.NewDifferenceIterator(a, b)
|
it, _ := trie.NewDifferenceIterator(a, b)
|
||||||
|
|
||||||
for {
|
if sdb.config.PathsAndProofs {
|
||||||
log.Debug("Current Path and Hash", "path", pathToStr(it), "hashold", it.Hash())
|
for {
|
||||||
if it.Leaf() {
|
log.Debug("Current Path and Hash", "path", pathToStr(it), "hashold", it.Hash())
|
||||||
leafProof := make([][]byte, len(it.LeafProof()))
|
if it.Leaf() {
|
||||||
copy(leafProof, it.LeafProof())
|
leafProof := make([][]byte, len(it.LeafProof()))
|
||||||
leafPath := make([]byte, len(it.Path()))
|
copy(leafProof, it.LeafProof())
|
||||||
copy(leafPath, it.Path())
|
leafPath := make([]byte, len(it.Path()))
|
||||||
leafKey := make([]byte, len(it.LeafKey()))
|
copy(leafPath, it.Path())
|
||||||
copy(leafKey, it.LeafKey())
|
leafKey := make([]byte, len(it.LeafKey()))
|
||||||
leafKeyHash := common.BytesToHash(leafKey)
|
copy(leafKey, it.LeafKey())
|
||||||
leafValue := make([]byte, len(it.LeafBlob()))
|
leafKeyHash := common.BytesToHash(leafKey)
|
||||||
copy(leafValue, it.LeafBlob())
|
leafValue := make([]byte, len(it.LeafBlob()))
|
||||||
// lookup account state
|
copy(leafValue, it.LeafBlob())
|
||||||
var account state.Account
|
// lookup account state
|
||||||
if err := rlp.DecodeBytes(leafValue, &account); err != nil {
|
var account state.Account
|
||||||
log.Error("Error looking up account via address", "address", leafKeyHash, "error", err)
|
if err := rlp.DecodeBytes(leafValue, &account); err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("error looking up account via address: %s, error: %v", leafKeyHash.Hex(), err)
|
||||||
|
}
|
||||||
|
aw := accountWrapper{
|
||||||
|
Account: account,
|
||||||
|
RawKey: leafKey,
|
||||||
|
RawValue: leafValue,
|
||||||
|
Proof: leafProof,
|
||||||
|
Path: leafPath,
|
||||||
|
}
|
||||||
|
// record account to diffs (creation if we are looking at new - old; deletion if old - new)
|
||||||
|
log.Debug("Account lookup successful", "address", leafKeyHash, "account", account)
|
||||||
|
diffAccounts[leafKeyHash] = aw
|
||||||
}
|
}
|
||||||
aw := accountWrapper{
|
cont := it.Next(true)
|
||||||
Account: account,
|
if !cont {
|
||||||
RawKey: leafKey,
|
break
|
||||||
RawValue: leafValue,
|
|
||||||
Proof: leafProof,
|
|
||||||
Path: leafPath,
|
|
||||||
}
|
}
|
||||||
// record account to diffs (creation if we are looking at new - old; deletion if old - new)
|
|
||||||
log.Debug("Account lookup successful", "address", leafKeyHash, "account", account)
|
|
||||||
diffAccounts[leafKeyHash] = aw
|
|
||||||
}
|
}
|
||||||
cont := it.Next(true)
|
} else {
|
||||||
if !cont {
|
for {
|
||||||
break
|
log.Debug("Current Path and Hash", "path", pathToStr(it), "old hash", it.Hash())
|
||||||
|
if it.Leaf() {
|
||||||
|
leafKey := make([]byte, len(it.LeafKey()))
|
||||||
|
copy(leafKey, it.LeafKey())
|
||||||
|
leafKeyHash := common.BytesToHash(leafKey)
|
||||||
|
leafValue := make([]byte, len(it.LeafBlob()))
|
||||||
|
copy(leafValue, it.LeafBlob())
|
||||||
|
// lookup account state
|
||||||
|
var account state.Account
|
||||||
|
if err := rlp.DecodeBytes(leafValue, &account); err != nil {
|
||||||
|
return nil, fmt.Errorf("error looking up account via address: %s, error: %v", leafKeyHash.Hex(), err)
|
||||||
|
}
|
||||||
|
aw := accountWrapper{
|
||||||
|
Account: account,
|
||||||
|
RawKey: leafKey,
|
||||||
|
RawValue: leafValue,
|
||||||
|
Proof: nil,
|
||||||
|
Path: nil,
|
||||||
|
}
|
||||||
|
// record account to diffs (creation if we are looking at new - old; deletion if old - new)
|
||||||
|
log.Debug("Account lookup successful", "address", leafKeyHash, "account", account)
|
||||||
|
diffAccounts[leafKeyHash] = aw
|
||||||
|
}
|
||||||
|
cont := it.Next(true)
|
||||||
|
if !cont {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,8 +186,7 @@ func (sdb *builder) buildDiffEventual(accounts AccountsMap) (AccountDiffsMap, er
|
|||||||
for _, val := range accounts {
|
for _, val := range accounts {
|
||||||
storageDiffs, err := sdb.buildStorageDiffsEventual(val.Account.Root)
|
storageDiffs, err := sdb.buildStorageDiffsEventual(val.Account.Root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed building eventual storage diffs", "Address", common.BytesToHash(val.RawKey), "error", err)
|
return nil, fmt.Errorf("failed building eventual storage diffs for address: %s, error: %v", common.BytesToHash(val.RawKey), err)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
accountDiffs[common.BytesToHash(val.RawKey)] = AccountDiff{
|
accountDiffs[common.BytesToHash(val.RawKey)] = AccountDiff{
|
||||||
Key: val.RawKey,
|
Key: val.RawKey,
|
||||||
@ -209,7 +235,7 @@ func (sdb *builder) buildStorageDiffsEventual(sr common.Hash) ([]StorageDiff, er
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
it := sTrie.NodeIterator(make([]byte, 0))
|
it := sTrie.NodeIterator(make([]byte, 0))
|
||||||
storageDiffs := buildStorageDiffsFromTrie(it)
|
storageDiffs := sdb.buildStorageDiffsFromTrie(it)
|
||||||
return storageDiffs, nil
|
return storageDiffs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,35 +255,58 @@ func (sdb *builder) buildStorageDiffsIncremental(oldSR common.Hash, newSR common
|
|||||||
oldIt := oldTrie.NodeIterator(make([]byte, 0))
|
oldIt := oldTrie.NodeIterator(make([]byte, 0))
|
||||||
newIt := newTrie.NodeIterator(make([]byte, 0))
|
newIt := newTrie.NodeIterator(make([]byte, 0))
|
||||||
it, _ := trie.NewDifferenceIterator(oldIt, newIt)
|
it, _ := trie.NewDifferenceIterator(oldIt, newIt)
|
||||||
storageDiffs := buildStorageDiffsFromTrie(it)
|
storageDiffs := sdb.buildStorageDiffsFromTrie(it)
|
||||||
|
|
||||||
return storageDiffs, nil
|
return storageDiffs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildStorageDiffsFromTrie(it trie.NodeIterator) []StorageDiff {
|
func (sdb *builder) buildStorageDiffsFromTrie(it trie.NodeIterator) []StorageDiff {
|
||||||
storageDiffs := make([]StorageDiff, 0)
|
storageDiffs := make([]StorageDiff, 0)
|
||||||
for {
|
if sdb.config.PathsAndProofs {
|
||||||
log.Debug("Iterating over state at path ", "path", pathToStr(it))
|
for {
|
||||||
if it.Leaf() {
|
log.Debug("Iterating over state at path ", "path", pathToStr(it))
|
||||||
log.Debug("Found leaf in storage", "path", pathToStr(it))
|
if it.Leaf() {
|
||||||
leafProof := make([][]byte, len(it.LeafProof()))
|
log.Debug("Found leaf in storage", "path", pathToStr(it))
|
||||||
copy(leafProof, it.LeafProof())
|
leafProof := make([][]byte, len(it.LeafProof()))
|
||||||
leafPath := make([]byte, len(it.Path()))
|
copy(leafProof, it.LeafProof())
|
||||||
copy(leafPath, it.Path())
|
leafPath := make([]byte, len(it.Path()))
|
||||||
leafKey := make([]byte, len(it.LeafKey()))
|
copy(leafPath, it.Path())
|
||||||
copy(leafKey, it.LeafKey())
|
leafKey := make([]byte, len(it.LeafKey()))
|
||||||
leafValue := make([]byte, len(it.LeafBlob()))
|
copy(leafKey, it.LeafKey())
|
||||||
copy(leafValue, it.LeafBlob())
|
leafValue := make([]byte, len(it.LeafBlob()))
|
||||||
storageDiffs = append(storageDiffs, StorageDiff{
|
copy(leafValue, it.LeafBlob())
|
||||||
Key: leafKey,
|
storageDiffs = append(storageDiffs, StorageDiff{
|
||||||
Value: leafValue,
|
Key: leafKey,
|
||||||
Path: leafPath,
|
Value: leafValue,
|
||||||
Proof: leafProof,
|
Path: leafPath,
|
||||||
})
|
Proof: leafProof,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
cont := it.Next(true)
|
||||||
|
if !cont {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cont := it.Next(true)
|
} else {
|
||||||
if !cont {
|
for {
|
||||||
break
|
log.Debug("Iterating over state at path ", "path", pathToStr(it))
|
||||||
|
if it.Leaf() {
|
||||||
|
log.Debug("Found leaf in storage", "path", pathToStr(it))
|
||||||
|
leafKey := make([]byte, len(it.LeafKey()))
|
||||||
|
copy(leafKey, it.LeafKey())
|
||||||
|
leafValue := make([]byte, len(it.LeafBlob()))
|
||||||
|
copy(leafValue, it.LeafBlob())
|
||||||
|
storageDiffs = append(storageDiffs, StorageDiff{
|
||||||
|
Key: leafKey,
|
||||||
|
Value: leafValue,
|
||||||
|
Path: nil,
|
||||||
|
Proof: nil,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
cont := it.Next(true)
|
||||||
|
if !cont {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,10 @@ func TestBuilder(t *testing.T) {
|
|||||||
block1 = blockMap[block1Hash]
|
block1 = blockMap[block1Hash]
|
||||||
block2 = blockMap[block2Hash]
|
block2 = blockMap[block2Hash]
|
||||||
block3 = blockMap[block3Hash]
|
block3 = blockMap[block3Hash]
|
||||||
builder = statediff.NewBuilder(testhelpers.Testdb, chain)
|
config := statediff.Config{
|
||||||
|
PathsAndProofs: true,
|
||||||
|
}
|
||||||
|
builder = statediff.NewBuilder(testhelpers.Testdb, chain, config)
|
||||||
|
|
||||||
type arguments struct {
|
type arguments struct {
|
||||||
oldStateRoot common.Hash
|
oldStateRoot common.Hash
|
||||||
|
23
statediff/config.go
Normal file
23
statediff/config.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2019 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package statediff
|
||||||
|
|
||||||
|
// Config is used to carry in parameters from CLI configuration
|
||||||
|
type Config struct {
|
||||||
|
PathsAndProofs bool
|
||||||
|
LeafsOnly bool
|
||||||
|
}
|
@ -18,18 +18,17 @@ package statediff
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
@ -66,25 +65,12 @@ type Service struct {
|
|||||||
Subscriptions map[rpc.ID]Subscription
|
Subscriptions map[rpc.ID]Subscription
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscription struct holds our subscription channels
|
|
||||||
type Subscription struct {
|
|
||||||
PayloadChan chan<- Payload
|
|
||||||
QuitChan chan<- bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Payload packages the data to send to StateDiffingService subscriptions
|
|
||||||
type Payload struct {
|
|
||||||
BlockRlp []byte `json:"blockRlp" gencodec:"required"`
|
|
||||||
StateDiffRlp []byte `json:"stateDiffRlp" gencodec:"required"`
|
|
||||||
Err error `json:"error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStateDiffService creates a new StateDiffingService
|
// NewStateDiffService creates a new StateDiffingService
|
||||||
func NewStateDiffService(db ethdb.Database, blockChain *core.BlockChain) (*Service, error) {
|
func NewStateDiffService(db ethdb.Database, blockChain *core.BlockChain, config Config) (*Service, error) {
|
||||||
return &Service{
|
return &Service{
|
||||||
Mutex: sync.Mutex{},
|
Mutex: sync.Mutex{},
|
||||||
BlockChain: blockChain,
|
BlockChain: blockChain,
|
||||||
Builder: NewBuilder(db, blockChain),
|
Builder: NewBuilder(db, blockChain, config),
|
||||||
QuitChan: make(chan bool),
|
QuitChan: make(chan bool),
|
||||||
Subscriptions: make(map[rpc.ID]Subscription),
|
Subscriptions: make(map[rpc.ID]Subscription),
|
||||||
}, nil
|
}, nil
|
||||||
@ -109,70 +95,61 @@ func (sds *Service) APIs() []rpc.API {
|
|||||||
|
|
||||||
// Loop is the main processing method
|
// Loop is the main processing method
|
||||||
func (sds *Service) Loop(chainEventCh chan core.ChainEvent) {
|
func (sds *Service) Loop(chainEventCh chan core.ChainEvent) {
|
||||||
|
|
||||||
chainEventSub := sds.BlockChain.SubscribeChainEvent(chainEventCh)
|
chainEventSub := sds.BlockChain.SubscribeChainEvent(chainEventCh)
|
||||||
defer chainEventSub.Unsubscribe()
|
defer chainEventSub.Unsubscribe()
|
||||||
|
|
||||||
blocksCh := make(chan *types.Block, 10)
|
|
||||||
errCh := chainEventSub.Err()
|
errCh := chainEventSub.Err()
|
||||||
|
|
||||||
go func() {
|
|
||||||
HandleChainEventChLoop:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
//Notify chain event channel of events
|
|
||||||
case chainEvent := <-chainEventCh:
|
|
||||||
log.Debug("Event received from chainEventCh", "event", chainEvent)
|
|
||||||
blocksCh <- chainEvent.Block
|
|
||||||
//if node stopped
|
|
||||||
case err := <-errCh:
|
|
||||||
log.Warn("Error from chain event subscription, breaking loop.", "error", err)
|
|
||||||
close(sds.QuitChan)
|
|
||||||
break HandleChainEventChLoop
|
|
||||||
case <-sds.QuitChan:
|
|
||||||
break HandleChainEventChLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
//loop through chain events until no more
|
|
||||||
HandleBlockChLoop:
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case block := <-blocksCh:
|
//Notify chain event channel of events
|
||||||
currentBlock := block
|
case chainEvent := <-chainEventCh:
|
||||||
|
log.Debug("Event received from chainEventCh", "event", chainEvent)
|
||||||
|
currentBlock := chainEvent.Block
|
||||||
parentHash := currentBlock.ParentHash()
|
parentHash := currentBlock.ParentHash()
|
||||||
parentBlock := sds.BlockChain.GetBlockByHash(parentHash)
|
parentBlock := sds.BlockChain.GetBlockByHash(parentHash)
|
||||||
if parentBlock == nil {
|
if parentBlock == nil {
|
||||||
log.Error("Parent block is nil, skipping this block",
|
log.Error("Parent block is nil, skipping this block",
|
||||||
"parent block hash", parentHash.String(),
|
"parent block hash", parentHash.String(),
|
||||||
"current block number", currentBlock.Number())
|
"current block number", currentBlock.Number())
|
||||||
break HandleBlockChLoop
|
continue
|
||||||
}
|
}
|
||||||
|
if err := sds.process(currentBlock, parentBlock); err != nil {
|
||||||
stateDiff, err := sds.Builder.BuildStateDiff(parentBlock.Root(), currentBlock.Root(), currentBlock.Number().Int64(), currentBlock.Hash())
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error building statediff", "block number", currentBlock.Number(), "error", err)
|
log.Error("Error building statediff", "block number", currentBlock.Number(), "error", err)
|
||||||
}
|
}
|
||||||
rlpBuff := new(bytes.Buffer)
|
case err := <-errCh:
|
||||||
currentBlock.EncodeRLP(rlpBuff)
|
log.Warn("Error from chain event subscription, breaking loop.", "error", err)
|
||||||
blockRlp := rlpBuff.Bytes()
|
sds.close()
|
||||||
stateDiffRlp, _ := rlp.EncodeToBytes(stateDiff)
|
return
|
||||||
payload := Payload{
|
|
||||||
BlockRlp: blockRlp,
|
|
||||||
StateDiffRlp: stateDiffRlp,
|
|
||||||
Err: err,
|
|
||||||
}
|
|
||||||
// If we have any websocket subscription listening in, send the data to them
|
|
||||||
sds.send(payload)
|
|
||||||
case <-sds.QuitChan:
|
case <-sds.QuitChan:
|
||||||
log.Debug("Quitting the statediff block channel")
|
log.Info("Quitting the statediff block channel")
|
||||||
sds.close()
|
sds.close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// process method builds the state diff payload from the current and parent block and streams it to listening subscriptions
|
||||||
|
func (sds *Service) process(currentBlock, parentBlock *types.Block) error {
|
||||||
|
stateDiff, err := sds.Builder.BuildStateDiff(parentBlock.Root(), currentBlock.Root(), currentBlock.Number().Int64(), currentBlock.Hash())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rlpBuff := new(bytes.Buffer)
|
||||||
|
currentBlock.EncodeRLP(rlpBuff)
|
||||||
|
blockRlp := rlpBuff.Bytes()
|
||||||
|
stateDiffBytes, _ := json.Marshal(stateDiff)
|
||||||
|
payload := Payload{
|
||||||
|
BlockRlp: blockRlp,
|
||||||
|
StateDiff: stateDiffBytes,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have any websocket subscription listening in, send the data to them
|
||||||
|
sds.send(payload)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Subscribe is used by the API to subscribe to the StateDiffingService loop
|
// Subscribe is used by the API to subscribe to the StateDiffingService loop
|
||||||
func (sds *Service) Subscribe(id rpc.ID, sub chan<- Payload, quitChan chan<- bool) {
|
func (sds *Service) Subscribe(id rpc.ID, sub chan<- Payload, quitChan chan<- bool) {
|
||||||
log.Info("Subscribing to the statediff service")
|
log.Info("Subscribing to the statediff service")
|
||||||
|
@ -18,6 +18,7 @@ package mocks
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
@ -26,7 +27,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/statediff"
|
"github.com/ethereum/go-ethereum/statediff"
|
||||||
)
|
)
|
||||||
@ -63,7 +63,6 @@ func (sds *MockStateDiffService) APIs() []rpc.API {
|
|||||||
// Loop mock method
|
// Loop mock method
|
||||||
func (sds *MockStateDiffService) Loop(chan core.ChainEvent) {
|
func (sds *MockStateDiffService) Loop(chan core.ChainEvent) {
|
||||||
//loop through chain events until no more
|
//loop through chain events until no more
|
||||||
HandleBlockChLoop:
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case block := <-sds.BlockChan:
|
case block := <-sds.BlockChan:
|
||||||
@ -74,7 +73,7 @@ HandleBlockChLoop:
|
|||||||
log.Error("Parent block is nil, skipping this block",
|
log.Error("Parent block is nil, skipping this block",
|
||||||
"parent block hash", parentHash.String(),
|
"parent block hash", parentHash.String(),
|
||||||
"current block number", currentBlock.Number())
|
"current block number", currentBlock.Number())
|
||||||
break HandleBlockChLoop
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
stateDiff, err := sds.Builder.BuildStateDiff(parentBlock.Root(), currentBlock.Root(), currentBlock.Number().Int64(), currentBlock.Hash())
|
stateDiff, err := sds.Builder.BuildStateDiff(parentBlock.Root(), currentBlock.Root(), currentBlock.Number().Int64(), currentBlock.Hash())
|
||||||
@ -84,11 +83,11 @@ HandleBlockChLoop:
|
|||||||
rlpBuff := new(bytes.Buffer)
|
rlpBuff := new(bytes.Buffer)
|
||||||
currentBlock.EncodeRLP(rlpBuff)
|
currentBlock.EncodeRLP(rlpBuff)
|
||||||
blockRlp := rlpBuff.Bytes()
|
blockRlp := rlpBuff.Bytes()
|
||||||
stateDiffRlp, _ := rlp.EncodeToBytes(stateDiff)
|
stateDiffBytes, _ := json.Marshal(stateDiff)
|
||||||
payload := statediff.Payload{
|
payload := statediff.Payload{
|
||||||
BlockRlp: blockRlp,
|
BlockRlp: blockRlp,
|
||||||
StateDiffRlp: stateDiffRlp,
|
StateDiff: stateDiffBytes,
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
// If we have any websocket subscription listening in, send the data to them
|
// If we have any websocket subscription listening in, send the data to them
|
||||||
sds.send(payload)
|
sds.send(payload)
|
||||||
|
@ -18,6 +18,7 @@ package mocks
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
@ -63,9 +64,12 @@ func TestAPI(t *testing.T) {
|
|||||||
blockChan := make(chan *types.Block)
|
blockChan := make(chan *types.Block)
|
||||||
parentBlockChain := make(chan *types.Block)
|
parentBlockChain := make(chan *types.Block)
|
||||||
serviceQuitChan := make(chan bool)
|
serviceQuitChan := make(chan bool)
|
||||||
|
config := statediff.Config{
|
||||||
|
PathsAndProofs: true,
|
||||||
|
}
|
||||||
mockService := MockStateDiffService{
|
mockService := MockStateDiffService{
|
||||||
Mutex: sync.Mutex{},
|
Mutex: sync.Mutex{},
|
||||||
Builder: statediff.NewBuilder(testhelpers.Testdb, chain),
|
Builder: statediff.NewBuilder(testhelpers.Testdb, chain, config),
|
||||||
BlockChan: blockChan,
|
BlockChan: blockChan,
|
||||||
ParentBlockChan: parentBlockChain,
|
ParentBlockChan: parentBlockChain,
|
||||||
QuitChan: serviceQuitChan,
|
QuitChan: serviceQuitChan,
|
||||||
@ -79,7 +83,7 @@ func TestAPI(t *testing.T) {
|
|||||||
blockChan <- block1
|
blockChan <- block1
|
||||||
parentBlockChain <- block0
|
parentBlockChain <- block0
|
||||||
expectedBlockRlp, _ := rlp.EncodeToBytes(block1)
|
expectedBlockRlp, _ := rlp.EncodeToBytes(block1)
|
||||||
expectedStateDiff := &statediff.StateDiff{
|
expectedStateDiff := statediff.StateDiff{
|
||||||
BlockNumber: block1.Number().Int64(),
|
BlockNumber: block1.Number().Int64(),
|
||||||
BlockHash: block1.Hash(),
|
BlockHash: block1.Hash(),
|
||||||
CreatedAccounts: statediff.AccountDiffsMap{
|
CreatedAccounts: statediff.AccountDiffsMap{
|
||||||
@ -112,14 +116,20 @@ func TestAPI(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectedStateDiffRlp, _ := rlp.EncodeToBytes(expectedStateDiff)
|
expectedStateDiffBytes, err := json.Marshal(expectedStateDiff)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case payload := <-payloadChan:
|
case payload := <-payloadChan:
|
||||||
if !bytes.Equal(payload.BlockRlp, expectedBlockRlp) {
|
if !bytes.Equal(payload.BlockRlp, expectedBlockRlp) {
|
||||||
t.Errorf("payload does not have expected block\r\actual: %v\r\nexpected: %v", payload.BlockRlp, expectedBlockRlp)
|
t.Errorf("payload does not have expected block\r\actual: %v\r\nexpected: %v", payload.BlockRlp, expectedBlockRlp)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(payload.StateDiffRlp, expectedStateDiffRlp) {
|
if !bytes.Equal(payload.StateDiff, expectedStateDiffBytes) {
|
||||||
t.Errorf("payload does not have expected state diff\r\actual: %v\r\nexpected: %v", payload.StateDiffRlp, expectedStateDiffRlp)
|
t.Errorf("payload does not have expected state diff\r\actual: %v\r\nexpected: %v", payload.StateDiff, expectedStateDiffBytes)
|
||||||
|
}
|
||||||
|
if payload.Err != nil {
|
||||||
|
t.Errorf("payload should not contain an error, but does: %v", payload.Err)
|
||||||
}
|
}
|
||||||
case <-quitChan:
|
case <-quitChan:
|
||||||
t.Errorf("channel quit before delivering payload")
|
t.Errorf("channel quit before delivering payload")
|
||||||
|
@ -26,6 +26,19 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Subscription struct holds our subscription channels
|
||||||
|
type Subscription struct {
|
||||||
|
PayloadChan chan<- Payload
|
||||||
|
QuitChan chan<- bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload packages the data to send to StateDiffingService subscriptions
|
||||||
|
type Payload struct {
|
||||||
|
BlockRlp []byte `json:"blockRlp" gencodec:"required"`
|
||||||
|
StateDiff []byte `json:"stateDiff" gencodec:"required"`
|
||||||
|
Err error `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
// AccountsMap is a mapping of keccak256(address) => accountWrapper
|
// AccountsMap is a mapping of keccak256(address) => accountWrapper
|
||||||
type AccountsMap map[common.Hash]accountWrapper
|
type AccountsMap map[common.Hash]accountWrapper
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user