fix service.go: prevent panics on subtrie iterators with odd-length prefix-paths

This commit is contained in:
i-norden 2023-05-12 10:06:54 -05:00
parent 3d8064ccbb
commit 4081787b03

View File

@ -219,10 +219,10 @@ func (s *Service) createSnapshot(ctx context.Context, it trie.NodeIterator, head
// end path for the concurrent iterator // end path for the concurrent iterator
var endPath []byte var endPath []byte
if iter, ok := it.(*trackedIter); ok { if i, ok := it.(*trackedIter); ok {
seekedPath = &iter.seekedPath seekedPath = &i.seekedPath
recoveredPath = append(recoveredPath, *seekedPath...) recoveredPath = append(recoveredPath, *seekedPath...)
endPath = iter.endPath endPath = i.endPath
} else { } else {
return errors.New("untracked iterator") return errors.New("untracked iterator")
} }
@ -257,7 +257,7 @@ func (s *Service) createSubTrieSnapshot(ctx context.Context, tx Tx, prefixPath [
// if node path is empty and prefix is nil, it's the root node // if node path is empty and prefix is nil, it's the root node
if prefixPath == nil { if prefixPath == nil {
// create snapshot of node, if it is a leaf this will also create snapshot of entire storage trie // create snapshot of node, if it is a leaf this will also create snapshot of entire storage trie
if err := s.createNodeSnapshot(tx, subTrieIt, headerID, height, seekingPaths); err != nil { if err := s.createNodeSnapshot(tx, subTrieIt, headerID, height, seekingPaths, prefixPath); err != nil {
return err return err
} }
updateSeekedPath(seekedPath, subTrieIt.Path()) updateSeekedPath(seekedPath, subTrieIt.Path())
@ -307,7 +307,7 @@ func (s *Service) createSubTrieSnapshot(ctx context.Context, tx Tx, prefixPath [
// if the node is along paths of interest // if the node is along paths of interest
// create snapshot of node, if it is a leaf this will also create snapshot of entire storage trie // create snapshot of node, if it is a leaf this will also create snapshot of entire storage trie
if err := s.createNodeSnapshot(tx, subTrieIt, headerID, height, seekingPaths); err != nil { if err := s.createNodeSnapshot(tx, subTrieIt, headerID, height, seekingPaths, prefixPath); err != nil {
return err return err
} }
// update seeked path after node has been processed // update seeked path after node has been processed
@ -358,7 +358,8 @@ func (s *Service) createSubTrieIt(prefixPath []byte, hash common.Hash, recovered
// createNodeSnapshot indexes the current node // createNodeSnapshot indexes the current node
// entire storage trie is also indexed (if available) // entire storage trie is also indexed (if available)
func (s *Service) createNodeSnapshot(tx Tx, it trie.NodeIterator, headerID string, height *big.Int, watchedAddressesLeafPaths [][]byte) error { func (s *Service) createNodeSnapshot(tx Tx, it trie.NodeIterator, headerID string, height *big.Int,
watchedAddressesLeafPaths [][]byte, prefixPath []byte) error {
tx, err := s.ipfsPublisher.PrepareTxForBatch(tx, s.maxBatchSize) tx, err := s.ipfsPublisher.PrepareTxForBatch(tx, s.maxBatchSize)
if err != nil { if err != nil {
return err return err
@ -369,7 +370,7 @@ func (s *Service) createNodeSnapshot(tx Tx, it trie.NodeIterator, headerID strin
// if it is a "value" node, we will index the value by leaf key // if it is a "value" node, we will index the value by leaf key
// publish codehash => code mappings // publish codehash => code mappings
// take storage snapshot // take storage snapshot
if err := s.processStateValueNode(it, headerID, height, watchedAddressesLeafPaths, tx); err != nil { if err := s.processStateValueNode(it, headerID, height, prefixPath, watchedAddressesLeafPaths, tx); err != nil {
return err return err
} }
} else { // trie nodes will be written to blockstore only } else { // trie nodes will be written to blockstore only
@ -391,8 +392,8 @@ func (s *Service) createNodeSnapshot(tx Tx, it trie.NodeIterator, headerID strin
return err return err
} }
if ok { if ok {
nodePath := make([]byte, len(it.Path())) // create the full node path as it.Path() doesn't include the path before subtrie root
copy(nodePath, it.Path()) nodePath := append(prefixPath, it.Path()...)
partialPath := trie.CompactToHex(elements[0].([]byte)) partialPath := trie.CompactToHex(elements[0].([]byte))
valueNodePath := append(nodePath, partialPath...) valueNodePath := append(nodePath, partialPath...)
if !isWatchedAddress(watchedAddressesLeafPaths, valueNodePath) { if !isWatchedAddress(watchedAddressesLeafPaths, valueNodePath) {
@ -403,7 +404,7 @@ func (s *Service) createNodeSnapshot(tx Tx, it trie.NodeIterator, headerID strin
} }
nodeHash := make([]byte, len(it.Hash().Bytes())) nodeHash := make([]byte, len(it.Hash().Bytes()))
copy(nodeHash, it.Hash().Bytes()) copy(nodeHash, it.Hash().Bytes())
if _, err := s.ipfsPublisher.PublishIPLD(ipld.Keccak256ToCid(ipld.MEthStateTrie, nodeHash), nodeVal, height, tx); err != nil { if err := s.ipfsPublisher.PublishIPLD(ipld.Keccak256ToCid(ipld.MEthStateTrie, nodeHash), nodeVal, height, tx); err != nil {
return err return err
} }
} }
@ -412,11 +413,13 @@ func (s *Service) createNodeSnapshot(tx Tx, it trie.NodeIterator, headerID strin
} }
// reminder: it.Leaf() == true when the iterator is positioned at a "value node" which is not something that actually exists in an MMPT // reminder: it.Leaf() == true when the iterator is positioned at a "value node" which is not something that actually exists in an MMPT
func (s *Service) processStateValueNode(it trie.NodeIterator, headerID string, height *big.Int, func (s *Service) processStateValueNode(it trie.NodeIterator, headerID string, height *big.Int, prefixPath []byte,
watchedAddressesLeafPaths [][]byte, tx Tx) error { watchedAddressesLeafPaths [][]byte, tx Tx) error {
// create the full node path as it.Path() doesn't include the path before subtrie root
nodePath := append(prefixPath, it.Path()...)
// skip if it is not a watched address // skip if it is not a watched address
// If we aren't watching any specific addresses, we are watching everything // If we aren't watching any specific addresses, we are watching everything
if len(watchedAddressesLeafPaths) > 0 && !isWatchedAddress(watchedAddressesLeafPaths, it.Path()) { if len(watchedAddressesLeafPaths) > 0 && !isWatchedAddress(watchedAddressesLeafPaths, nodePath) {
return nil return nil
} }
@ -428,8 +431,24 @@ func (s *Service) processStateValueNode(it trie.NodeIterator, headerID string, h
if err := rlp.DecodeBytes(accountRLP, &account); err != nil { if err := rlp.DecodeBytes(accountRLP, &account); err != nil {
return fmt.Errorf("error decoding account for leaf value at leaf key %x\nerror: %v", it.LeafKey(), err) return fmt.Errorf("error decoding account for leaf value at leaf key %x\nerror: %v", it.LeafKey(), err)
} }
leafKey := make([]byte, len(it.LeafKey()))
copy(leafKey, it.LeafKey()) // since this is a "value node", we need to move up to the "parent" node which is the actual leaf node
// it should be in the fastcache since it necessarily was recently accessed to reach the current "node"
parentNodeRLP, err := s.stateDB.TrieDB().Node(it.Parent())
if err != nil {
return err
}
var nodeElements []interface{}
if err = rlp.DecodeBytes(parentNodeRLP, &nodeElements); err != nil {
return err
}
parentSubPath := make([]byte, len(it.ParentPath()))
copy(parentSubPath, it.ParentPath())
parentPath := append(prefixPath, parentSubPath...)
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
valueNodePath := append(parentPath, partialPath...)
encodedPath := trie.HexToCompact(valueNodePath)
leafKey := encodedPath[1:]
// write codehash => code mappings if we have a contract // write codehash => code mappings if we have a contract
if !bytes.Equal(account.CodeHash, emptyCodeHash) { if !bytes.Equal(account.CodeHash, emptyCodeHash) {
@ -438,17 +457,11 @@ func (s *Service) processStateValueNode(it trie.NodeIterator, headerID string, h
if err != nil { if err != nil {
return fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %v", codeHash.String(), err) return fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %v", codeHash.String(), err)
} }
if _, err := s.ipfsPublisher.PublishIPLD(ipld.Keccak256ToCid(ipld.RawBinary, codeHash.Bytes()), code, height, tx); err != nil { if err := s.ipfsPublisher.PublishIPLD(ipld.Keccak256ToCid(ipld.RawBinary, codeHash.Bytes()), code, height, tx); err != nil {
return err return err
} }
} }
// since this is a "value node", we need to move up to the "parent" node which is the actual leaf node
// it should be in the fastcache since it necessarily was recently accessed to reach the current node
parentNodeRLP, err := s.stateDB.TrieDB().Node(it.Parent())
if err != nil {
return err
}
// publish the state leaf model // publish the state leaf model
stateKeyStr := common.BytesToHash(leafKey).String() stateKeyStr := common.BytesToHash(leafKey).String()
stateLeafNodeModel := &models.StateNodeModel{ stateLeafNodeModel := &models.StateNodeModel{
@ -495,7 +508,7 @@ func (s *Service) storageSnapshot(sr common.Hash, stateKey, headerID string, hei
copy(nodeVal, it.NodeBlob()) copy(nodeVal, it.NodeBlob())
nodeHash := make([]byte, len(it.Hash().Bytes())) nodeHash := make([]byte, len(it.Hash().Bytes()))
copy(nodeHash, it.Hash().Bytes()) copy(nodeHash, it.Hash().Bytes())
if _, err := s.ipfsPublisher.PublishIPLD(ipld.Keccak256ToCid(ipld.MEthStorageTrie, nodeHash), nodeVal, height, tx); err != nil { if err := s.ipfsPublisher.PublishIPLD(ipld.Keccak256ToCid(ipld.MEthStorageTrie, nodeHash), nodeVal, height, tx); err != nil {
return nil, err return nil, err
} }
} }