gitlab.com/yannislg/go-pulse@v0.0.0-20210722055913-a3e24e95638d/core/state/snapshot/conversion.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package snapshot
    18  
    19  import (
    20  	"sync"
    21  	"time"
    22  
    23  	"github.com/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/ethdb/memorydb"
    25  	"github.com/ethereum/go-ethereum/log"
    26  	"github.com/ethereum/go-ethereum/rlp"
    27  	"github.com/ethereum/go-ethereum/trie"
    28  )
    29  
    30  // conversionAccount is used for converting between full and slim format. When
    31  // doing this, we can consider 'balance' as a byte array, as it has already
    32  // been converted from big.Int into an rlp-byteslice.
    33  type conversionAccount struct {
    34  	Nonce    uint64
    35  	Balance  []byte
    36  	Root     []byte
    37  	CodeHash []byte
    38  }
    39  
    40  // SlimToFull converts data on the 'slim RLP' format into the full RLP-format
    41  func SlimToFull(data []byte) ([]byte, error) {
    42  	acc := &conversionAccount{}
    43  	if err := rlp.DecodeBytes(data, acc); err != nil {
    44  		return nil, err
    45  	}
    46  	if len(acc.Root) == 0 {
    47  		acc.Root = emptyRoot[:]
    48  	}
    49  	if len(acc.CodeHash) == 0 {
    50  		acc.CodeHash = emptyCode[:]
    51  	}
    52  	fullData, err := rlp.EncodeToBytes(acc)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	return fullData, nil
    57  }
    58  
    59  // trieKV represents a trie key-value pair
    60  type trieKV struct {
    61  	key   common.Hash
    62  	value []byte
    63  }
    64  
    65  type trieGeneratorFn func(in chan (trieKV), out chan (common.Hash))
    66  
    67  // GenerateTrieRoot takes an account iterator and reproduces the root hash.
    68  func GenerateTrieRoot(it AccountIterator) common.Hash {
    69  	return generateTrieRoot(it, stdGenerate)
    70  }
    71  
    72  func generateTrieRoot(it AccountIterator, generatorFn trieGeneratorFn) common.Hash {
    73  	var (
    74  		in  = make(chan trieKV)      // chan to pass leaves
    75  		out = make(chan common.Hash) // chan to collect result
    76  		wg  sync.WaitGroup
    77  	)
    78  	wg.Add(1)
    79  	go func() {
    80  		generatorFn(in, out)
    81  		wg.Done()
    82  	}()
    83  	// Feed leaves
    84  	start := time.Now()
    85  	logged := time.Now()
    86  	accounts := 0
    87  	for it.Next() {
    88  		slimData := it.Account()
    89  		fullData, _ := SlimToFull(slimData)
    90  		l := trieKV{it.Hash(), fullData}
    91  		in <- l
    92  		if time.Since(logged) > 8*time.Second {
    93  			log.Info("Generating trie hash from snapshot",
    94  				"at", l.key, "accounts", accounts, "elapsed", time.Since(start))
    95  			logged = time.Now()
    96  		}
    97  		accounts++
    98  	}
    99  	close(in)
   100  	result := <-out
   101  	log.Info("Generated trie hash from snapshot", "accounts", accounts, "elapsed", time.Since(start))
   102  	wg.Wait()
   103  	return result
   104  }
   105  
   106  // stdGenerate is a very basic hexary trie builder which uses the same Trie
   107  // as the rest of geth, with no enhancements or optimizations
   108  func stdGenerate(in chan (trieKV), out chan (common.Hash)) {
   109  	t, _ := trie.New(common.Hash{}, trie.NewDatabase(memorydb.New()))
   110  	for leaf := range in {
   111  		t.TryUpdate(leaf.key[:], leaf.value)
   112  	}
   113  	out <- t.Hash()
   114  }