[WIP] Preimages #42

Closed
ramilexe wants to merge 1 commits from preimages_v2 into v1.9.24-statediff
6 changed files with 77 additions and 28 deletions

View File

@ -22,6 +22,7 @@ package statediff
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
@ -44,7 +45,7 @@ var (
type Builder interface { type Builder interface {
BuildStateDiffObject(args Args, params Params) (StateObject, error) BuildStateDiffObject(args Args, params Params) (StateObject, error)
BuildStateTrieObject(current *types.Block) (StateObject, error) BuildStateTrieObject(current *types.Block) (StateObject, error)
WriteStateDiffObject(args StateRoots, params Params, output StateNodeSink, codeOutput CodeSink) error WriteStateDiffObject(args StateRoots, params Params, output StateNodeSink, codeOutput CodeSink, addressOutput AddressSink) error
} }
type builder struct { type builder struct {
@ -92,6 +93,12 @@ func codeMappingAppender(codeAndCodeHashes *[]CodeAndCodeHash) CodeSink {
return nil return nil
} }
} }
func addressMappingAppender(addressAndCodeHashes *[]AddressAndAddressHash) AddressSink {
return func(a AddressAndAddressHash) error {
*addressAndCodeHashes = append(*addressAndCodeHashes, a)
return nil
}
}
// NewBuilder is used to create a statediff builder // NewBuilder is used to create a statediff builder
func NewBuilder(stateCache state.Database) Builder { func NewBuilder(stateCache state.Database) Builder {
@ -107,7 +114,7 @@ func (sdb *builder) BuildStateTrieObject(current *types.Block) (StateObject, err
return StateObject{}, fmt.Errorf("error creating trie for block %d: %v", current.Number(), err) return StateObject{}, fmt.Errorf("error creating trie for block %d: %v", current.Number(), err)
} }
it := currentTrie.NodeIterator([]byte{}) it := currentTrie.NodeIterator([]byte{})
stateNodes, codeAndCodeHashes, err := sdb.buildStateTrie(it) stateNodes, codeAndCodeHashes, addressAndAddressHashes, err := sdb.buildStateTrie(it)
if err != nil { if err != nil {
return StateObject{}, fmt.Errorf("error collecting state nodes for block %d: %v", current.Number(), err) return StateObject{}, fmt.Errorf("error collecting state nodes for block %d: %v", current.Number(), err)
} }
@ -116,12 +123,14 @@ func (sdb *builder) BuildStateTrieObject(current *types.Block) (StateObject, err
BlockHash: current.Hash(), BlockHash: current.Hash(),
Nodes: stateNodes, Nodes: stateNodes,
CodeAndCodeHashes: codeAndCodeHashes, CodeAndCodeHashes: codeAndCodeHashes,
AddressAndAddressHashes: addressAndAddressHashes,
}, nil }, nil
} }
func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]StateNode, []CodeAndCodeHash, error) { func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]StateNode, []CodeAndCodeHash, []AddressAndAddressHash, error) {
stateNodes := make([]StateNode, 0) stateNodes := make([]StateNode, 0)
codeAndCodeHashes := make([]CodeAndCodeHash, 0) codeAndCodeHashes := make([]CodeAndCodeHash, 0)
addressAndAddressHashes := make([]AddressAndAddressHash, 0)
for it.Next(true) { for it.Next(true) {
// skip value nodes // skip value nodes
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) { if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
@ -129,31 +138,32 @@ func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]StateNode, []CodeAnd
} }
node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB()) node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB())
if err != nil { if err != nil {
return nil, nil, err return nil, nil, nil, err
} }
switch node.NodeType { switch node.NodeType {
case Leaf: case Leaf:
var account state.Account var account state.Account
if err := rlp.DecodeBytes(nodeElements[1].([]byte), &account); err != nil { if err := rlp.DecodeBytes(nodeElements[1].([]byte), &account); err != nil {
return nil, nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", node.Path, err) return nil, nil, nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", node.Path, err)
} }
partialPath := trie.CompactToHex(nodeElements[0].([]byte)) partialPath := trie.CompactToHex(nodeElements[0].([]byte))
valueNodePath := append(node.Path, partialPath...) valueNodePath := append(node.Path, partialPath...)
encodedPath := trie.HexToCompact(valueNodePath) encodedPath := trie.HexToCompact(valueNodePath)
leafKey := encodedPath[1:] leafKey := encodedPath[1:]
node.LeafKey = leafKey node.LeafKey = leafKey
// derive code by code hash
if !bytes.Equal(account.CodeHash, nullCodeHash) { if !bytes.Equal(account.CodeHash, nullCodeHash) {
var storageNodes []StorageNode var storageNodes []StorageNode
err := sdb.buildStorageNodesEventual(account.Root, nil, true, storageNodeAppender(&storageNodes)) err := sdb.buildStorageNodesEventual(account.Root, nil, true, storageNodeAppender(&storageNodes))
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed building eventual storage diffs for account %+v\r\nerror: %v", account, err) return nil, nil, nil, fmt.Errorf("failed building eventual storage diffs for account %+v\r\nerror: %v", account, err)
} }
node.StorageNodes = storageNodes node.StorageNodes = storageNodes
// emit codehash => code mappings for cod // emit codehash => code mappings for cod
codeHash := common.BytesToHash(account.CodeHash) codeHash := common.BytesToHash(account.CodeHash)
code, err := sdb.stateCache.ContractCode(common.Hash{}, codeHash) code, err := sdb.stateCache.ContractCode(common.Hash{}, codeHash)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %v", codeHash.String(), err) return nil, nil, nil, fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %v", codeHash.String(), err)
} }
codeAndCodeHashes = append(codeAndCodeHashes, CodeAndCodeHash{ codeAndCodeHashes = append(codeAndCodeHashes, CodeAndCodeHash{
Hash: codeHash, Hash: codeHash,
@ -164,19 +174,20 @@ func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]StateNode, []CodeAnd
case Extension, Branch: case Extension, Branch:
stateNodes = append(stateNodes, node) stateNodes = append(stateNodes, node)
default: default:
return nil, nil, fmt.Errorf("unexpected node type %s", node.NodeType) return nil, nil, nil, fmt.Errorf("unexpected node type %s", node.NodeType)
} }
} }
return stateNodes, codeAndCodeHashes, it.Error() return stateNodes, codeAndCodeHashes, addressAndAddressHashes, it.Error()
} }
// BuildStateDiffObject builds a statediff object from two blocks and the provided parameters // BuildStateDiffObject builds a statediff object from two blocks and the provided parameters
func (sdb *builder) BuildStateDiffObject(args Args, params Params) (StateObject, error) { func (sdb *builder) BuildStateDiffObject(args Args, params Params) (StateObject, error) {
var stateNodes []StateNode var stateNodes []StateNode
var codeAndCodeHashes []CodeAndCodeHash var codeAndCodeHashes []CodeAndCodeHash
var addressAndCodeHashes []AddressAndAddressHash
err := sdb.WriteStateDiffObject( err := sdb.WriteStateDiffObject(
StateRoots{OldStateRoot: args.OldStateRoot, NewStateRoot: args.NewStateRoot}, StateRoots{OldStateRoot: args.OldStateRoot, NewStateRoot: args.NewStateRoot},
params, stateNodeAppender(&stateNodes), codeMappingAppender(&codeAndCodeHashes)) params, stateNodeAppender(&stateNodes), codeMappingAppender(&codeAndCodeHashes), addressMappingAppender(&addressAndCodeHashes))
if err != nil { if err != nil {
return StateObject{}, err return StateObject{}, err
} }
@ -189,16 +200,16 @@ func (sdb *builder) BuildStateDiffObject(args Args, params Params) (StateObject,
} }
// Writes a statediff object to output callback // Writes a statediff object to output callback
func (sdb *builder) WriteStateDiffObject(args StateRoots, params Params, output StateNodeSink, codeOutput CodeSink) error { func (sdb *builder) WriteStateDiffObject(args StateRoots, params Params, output StateNodeSink, codeOutput CodeSink, addressOutput AddressSink) error {
if !params.IntermediateStateNodes || len(params.WatchedAddresses) > 0 { if !params.IntermediateStateNodes || len(params.WatchedAddresses) > 0 {
// if we are watching only specific accounts then we are only diffing leaf nodes // if we are watching only specific accounts then we are only diffing leaf nodes
return sdb.buildStateDiffWithoutIntermediateStateNodes(args, params, output, codeOutput) return sdb.buildStateDiffWithoutIntermediateStateNodes(args, params, output, codeOutput, addressOutput)
} else { } else {
return sdb.buildStateDiffWithIntermediateStateNodes(args, params, output, codeOutput) return sdb.buildStateDiffWithIntermediateStateNodes(args, params, output, codeOutput, addressOutput)
} }
} }
func (sdb *builder) buildStateDiffWithIntermediateStateNodes(args StateRoots, params Params, output StateNodeSink, codeOutput CodeSink) error { func (sdb *builder) buildStateDiffWithIntermediateStateNodes(args StateRoots, params Params, output StateNodeSink, codeOutput CodeSink, addressOutput AddressSink) error {
// Load tries for old and new states // Load tries for old and new states
oldTrie, err := sdb.stateCache.OpenTrie(args.OldStateRoot) oldTrie, err := sdb.stateCache.OpenTrie(args.OldStateRoot)
if err != nil { if err != nil {
@ -246,14 +257,14 @@ func (sdb *builder) buildStateDiffWithIntermediateStateNodes(args StateRoots, pa
return fmt.Errorf("error building diff for updated accounts: %v", err) return fmt.Errorf("error building diff for updated accounts: %v", err)
} }
// build the diff nodes for created accounts // build the diff nodes for created accounts
err = sdb.buildAccountCreations(diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes, output, codeOutput) err = sdb.buildAccountCreations(diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes, output, codeOutput, addressOutput)
if err != nil { if err != nil {
return fmt.Errorf("error building diff for created accounts: %v", err) return fmt.Errorf("error building diff for created accounts: %v", err)
} }
return nil return nil
} }
func (sdb *builder) buildStateDiffWithoutIntermediateStateNodes(args StateRoots, params Params, output StateNodeSink, codeOutput CodeSink) error { func (sdb *builder) buildStateDiffWithoutIntermediateStateNodes(args StateRoots, params Params, output StateNodeSink, codeOutput CodeSink, addressOutput AddressSink) error {
// Load tries for old (A) and new (B) states // Load tries for old (A) and new (B) states
oldTrie, err := sdb.stateCache.OpenTrie(args.OldStateRoot) oldTrie, err := sdb.stateCache.OpenTrie(args.OldStateRoot)
if err != nil { if err != nil {
@ -300,7 +311,7 @@ func (sdb *builder) buildStateDiffWithoutIntermediateStateNodes(args StateRoots,
return fmt.Errorf("error building diff for updated accounts: %v", err) return fmt.Errorf("error building diff for updated accounts: %v", err)
} }
// build the diff nodes for created accounts // build the diff nodes for created accounts
err = sdb.buildAccountCreations(diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes, output, codeOutput) err = sdb.buildAccountCreations(diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes, output, codeOutput, addressOutput)
if err != nil { if err != nil {
return fmt.Errorf("error building diff for created accounts: %v", err) return fmt.Errorf("error building diff for created accounts: %v", err)
} }
@ -497,7 +508,7 @@ func (sdb *builder) buildAccountUpdates(creations, deletions AccountMap, updated
// buildAccountCreations returns the statediff node objects for all the accounts that exist at B but not at A // buildAccountCreations returns the statediff node objects for all the accounts that exist at B but not at A
// it also returns the code and codehash for created contract accounts // it also returns the code and codehash for created contract accounts
func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKeys []common.Hash, intermediateStorageNodes bool, output StateNodeSink, codeOutput CodeSink) error { func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKeys []common.Hash, intermediateStorageNodes bool, output StateNodeSink, codeOutput CodeSink, addressOutput AddressSink) error {
for _, val := range accounts { for _, val := range accounts {
diff := StateNode{ diff := StateNode{
NodeType: val.NodeType, NodeType: val.NodeType,
@ -526,6 +537,19 @@ func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKey
return err return err
} }
} }
// get address by hash
addressHash := common.BytesToHash(val.LeafKey)
address := rawdb.ReadPreimage(sdb.stateCache.TrieDB().DiskDB(), addressHash)
log.Info("address hash %s, address %s", addressHash.String(), address)
if err := addressOutput(AddressAndAddressHash{
Hash: addressHash,
Address: common.BytesToAddress(address),
}); err != nil {
return err
}
if err := output(diff); err != nil { if err := output(diff); err != nil {
return err return err
} }

View File

@ -46,6 +46,7 @@ type Indexer interface {
PushBlock(block *types.Block, receipts types.Receipts, totalDifficulty *big.Int) (*BlockTx, error) PushBlock(block *types.Block, receipts types.Receipts, totalDifficulty *big.Int) (*BlockTx, error)
PushStateNode(tx *BlockTx, stateNode sdtypes.StateNode) error PushStateNode(tx *BlockTx, stateNode sdtypes.StateNode) error
PushCodeAndCodeHash(tx *BlockTx, codeAndCodeHash sdtypes.CodeAndCodeHash) error PushCodeAndCodeHash(tx *BlockTx, codeAndCodeHash sdtypes.CodeAndCodeHash) error
PushAddressAndAddressHash(tx *BlockTx, addressAndCodeHash sdtypes.AddressAndAddressHash) error
} }
// StateDiffIndexer satisfies the Indexer interface for ethereum statediff objects // StateDiffIndexer satisfies the Indexer interface for ethereum statediff objects
@ -393,3 +394,16 @@ func (sdi *StateDiffIndexer) PushCodeAndCodeHash(tx *BlockTx, codeAndCodeHash sd
} }
return nil return nil
} }
// Publishes address and address hash pairs to the ipld database
func (sdi *StateDiffIndexer) PushAddressAndAddressHash(tx *BlockTx, addressAndCodeHash sdtypes.AddressAndAddressHash) error {
// codec doesn't matter since db key is multihash-based
mhKey, err := shared.MultihashKeyFromKeccak256(addressAndCodeHash.Hash)
if err != nil {
return err
}
if err := shared.PublishDirect(tx.dbtx, mhKey, addressAndCodeHash.Address.Bytes()); err != nil {
return err
}
return nil
}

View File

@ -542,10 +542,13 @@ func (sds *Service) writeStateDiff(block *types.Block, parentRoot common.Hash, p
codeOutput := func(c CodeAndCodeHash) error { codeOutput := func(c CodeAndCodeHash) error {
return sds.indexer.PushCodeAndCodeHash(tx, c) return sds.indexer.PushCodeAndCodeHash(tx, c)
} }
addressOutput := func(a AddressAndAddressHash) error {
return sds.indexer.PushAddressAndAddressHash(tx, a)
}
err = sds.Builder.WriteStateDiffObject(StateRoots{ err = sds.Builder.WriteStateDiffObject(StateRoots{
NewStateRoot: block.Root(), NewStateRoot: block.Root(),
OldStateRoot: parentRoot, OldStateRoot: parentRoot,
}, params, output, codeOutput) }, params, output, codeOutput, addressOutput)
// allow dereferencing of parent, keep current locked as it should be the next parent // allow dereferencing of parent, keep current locked as it should be the next parent
sds.BlockChain.UnlockTrie(parentRoot) sds.BlockChain.UnlockTrie(parentRoot)

View File

@ -42,7 +42,7 @@ func (builder *Builder) BuildStateDiffObject(args statediff.Args, params statedi
} }
// BuildStateDiffObject mock method // BuildStateDiffObject mock method
func (builder *Builder) WriteStateDiffObject(args statediff.StateRoots, params statediff.Params, output sdtypes.StateNodeSink, codeOutput sdtypes.CodeSink) error { func (builder *Builder) WriteStateDiffObject(args statediff.StateRoots, params statediff.Params, output sdtypes.StateNodeSink, codeOutput sdtypes.CodeSink, addressOutput sdtypes.AddressSink) error {
builder.StateRoots = args builder.StateRoots = args
builder.Params = params builder.Params = params

View File

@ -98,6 +98,7 @@ type StateObject struct {
BlockHash common.Hash `json:"blockHash" gencodec:"required"` BlockHash common.Hash `json:"blockHash" gencodec:"required"`
Nodes []types.StateNode `json:"nodes" gencodec:"required"` Nodes []types.StateNode `json:"nodes" gencodec:"required"`
CodeAndCodeHashes []types.CodeAndCodeHash `json:"codeMapping"` CodeAndCodeHashes []types.CodeAndCodeHash `json:"codeMapping"`
AddressAndAddressHashes []types.AddressAndAddressHash `json:"addressMapping"`
} }
// AccountMap is a mapping of hex encoded path => account wrapper // AccountMap is a mapping of hex encoded path => account wrapper

View File

@ -56,6 +56,13 @@ type CodeAndCodeHash struct {
Code []byte `json:"code"` Code []byte `json:"code"`
} }
// AddressAndAddressHash struct for holding keccak256(address) => address mapping
type AddressAndAddressHash struct {
Hash common.Hash `json:"addressHash"`
Address common.Address `json:"address"`
}
type StateNodeSink func(StateNode) error type StateNodeSink func(StateNode) error
type StorageNodeSink func(StorageNode) error type StorageNodeSink func(StorageNode) error
type CodeSink func(CodeAndCodeHash) error type CodeSink func(CodeAndCodeHash) error
type AddressSink func(AddressAndAddressHash) error