package integrity import ( "bytes" "encoding/binary" "fmt" "math/bits" "time" "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/dbutils" "github.com/ledgerwatch/turbo-geth/common/hexutil" "github.com/ledgerwatch/turbo-geth/ethdb" "github.com/ledgerwatch/turbo-geth/log" "github.com/ledgerwatch/turbo-geth/turbo/trie" ) // AssertSubset a & b == a - checks whether a is subset of b func AssertSubset(prefix []byte, a, b uint16) { if (a & b) != a { panic(fmt.Errorf("invariant 'is subset' failed: %x, %b, %b", prefix, a, b)) } } func Trie(tx ethdb.Tx, slowChecks bool, quit <-chan struct{}) { logEvery := time.NewTicker(10 * time.Second) defer logEvery.Stop() seek := make([]byte, 256) buf := make([]byte, 256) buf2 := make([]byte, 256) { c, trieAcc2, accC := tx.Cursor(dbutils.TrieOfAccountsBucket), tx.Cursor(dbutils.TrieOfAccountsBucket), tx.Cursor(dbutils.HashedAccountsBucket) defer c.Close() defer trieAcc2.Close() defer accC.Close() for k, v, errc := c.First(); k != nil; k, v, errc = c.Next() { if errc != nil { panic(errc) } select { default: case <-quit: return case <-logEvery.C: log.Info("trie account integrity", "key", fmt.Sprintf("%x", k)) } hasState, hasBranch, hasHash, hashes, _ := trie.UnmarshalTrieNode(v) AssertSubset(k, hasBranch, hasState) AssertSubset(k, hasHash, hasState) if bits.OnesCount16(hasHash) != len(hashes)/common.HashLength { panic(fmt.Errorf("invariant bits.OnesCount16(hasHash) == len(hashes) failed: %d, %d", bits.OnesCount16(hasHash), len(v[6:])/common.HashLength)) } found := false var parentK []byte // must have parent with right hasBranch bit for i := len(k) - 1; i > 0 && !found; i-- { parentK = k[:i] kParent, vParent, err := trieAcc2.SeekExact(parentK) if err != nil { panic(err) } if kParent == nil { continue } found = true parentHasBranch := binary.BigEndian.Uint16(vParent[2:]) parentHasBit := 1< 1 { panic(fmt.Errorf("trie hash %x has no parent", k)) } // must have all children seek = seek[:len(k)+1] copy(seek, k) for i := uint16(0); i < 16; i++ { if 1<nil", k, hasBranch, i, seek)) } if !bytes.HasPrefix(k2, seek) { panic(fmt.Errorf("key %x has branches %016b, but there is no child %d in db; last seen key: %x->%x", k, hasBranch, i, seek, k2)) } } if !slowChecks { continue } // each AccTrie must cover some state buf = buf[:len(k)+1] copy(buf, k) for i := uint16(0); i < 16; i++ { if 1<= 40 && !found; i-- { parentK = k[:i] kParent, vParent, err := trieStorage.SeekExact(parentK) if err != nil { panic(err) } if kParent == nil { continue } found = true parentBranches := binary.BigEndian.Uint16(vParent[2:]) parentHasBit := 1< 40 { panic(fmt.Errorf("trie hash %x has no parent. Last checked: %x", k, parentK)) } // must have all children seek = seek[:len(k)+1] copy(seek, k) for i := uint16(0); i < 16; i++ { if 1<