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 }