github.com/theQRL/go-zond@v0.1.1/trie/triedb/pathdb/history_test.go (about) 1 // Copyright 2022 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 pathdb 18 19 import ( 20 "bytes" 21 "fmt" 22 "reflect" 23 "testing" 24 25 "github.com/theQRL/go-zond/common" 26 "github.com/theQRL/go-zond/core/rawdb" 27 "github.com/theQRL/go-zond/core/types" 28 "github.com/theQRL/go-zond/zonddb" 29 "github.com/theQRL/go-zond/rlp" 30 "github.com/theQRL/go-zond/trie/testutil" 31 "github.com/theQRL/go-zond/trie/triestate" 32 ) 33 34 // randomStateSet generates a random state change set. 35 func randomStateSet(n int) *triestate.Set { 36 var ( 37 accounts = make(map[common.Address][]byte) 38 storages = make(map[common.Address]map[common.Hash][]byte) 39 ) 40 for i := 0; i < n; i++ { 41 addr := testutil.RandomAddress() 42 storages[addr] = make(map[common.Hash][]byte) 43 for j := 0; j < 3; j++ { 44 v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testutil.RandBytes(32))) 45 storages[addr][testutil.RandomHash()] = v 46 } 47 account := generateAccount(types.EmptyRootHash) 48 accounts[addr] = types.SlimAccountRLP(account) 49 } 50 return triestate.New(accounts, storages, nil) 51 } 52 53 func makeHistory() *history { 54 return newHistory(testutil.RandomHash(), types.EmptyRootHash, 0, randomStateSet(3)) 55 } 56 57 func makeHistories(n int) []*history { 58 var ( 59 parent = types.EmptyRootHash 60 result []*history 61 ) 62 for i := 0; i < n; i++ { 63 root := testutil.RandomHash() 64 h := newHistory(root, parent, uint64(i), randomStateSet(3)) 65 parent = root 66 result = append(result, h) 67 } 68 return result 69 } 70 71 func TestEncodeDecodeHistory(t *testing.T) { 72 var ( 73 m meta 74 dec history 75 obj = makeHistory() 76 ) 77 // check if meta data can be correctly encode/decode 78 blob := obj.meta.encode() 79 if err := m.decode(blob); err != nil { 80 t.Fatalf("Failed to decode %v", err) 81 } 82 if !reflect.DeepEqual(&m, obj.meta) { 83 t.Fatal("meta is mismatched") 84 } 85 86 // check if account/storage data can be correctly encode/decode 87 accountData, storageData, accountIndexes, storageIndexes := obj.encode() 88 if err := dec.decode(accountData, storageData, accountIndexes, storageIndexes); err != nil { 89 t.Fatalf("Failed to decode, err: %v", err) 90 } 91 if !compareSet(dec.accounts, obj.accounts) { 92 t.Fatal("account data is mismatched") 93 } 94 if !compareStorages(dec.storages, obj.storages) { 95 t.Fatal("storage data is mismatched") 96 } 97 if !compareList(dec.accountList, obj.accountList) { 98 t.Fatal("account list is mismatched") 99 } 100 if !compareStorageList(dec.storageList, obj.storageList) { 101 t.Fatal("storage list is mismatched") 102 } 103 } 104 105 func checkHistory(t *testing.T, db zonddb.KeyValueReader, freezer *rawdb.ResettableFreezer, id uint64, root common.Hash, exist bool) { 106 blob := rawdb.ReadStateHistoryMeta(freezer, id) 107 if exist && len(blob) == 0 { 108 t.Fatalf("Failed to load trie history, %d", id) 109 } 110 if !exist && len(blob) != 0 { 111 t.Fatalf("Unexpected trie history, %d", id) 112 } 113 if exist && rawdb.ReadStateID(db, root) == nil { 114 t.Fatalf("Root->ID mapping is not found, %d", id) 115 } 116 if !exist && rawdb.ReadStateID(db, root) != nil { 117 t.Fatalf("Unexpected root->ID mapping, %d", id) 118 } 119 } 120 121 func checkHistoriesInRange(t *testing.T, db zonddb.KeyValueReader, freezer *rawdb.ResettableFreezer, from, to uint64, roots []common.Hash, exist bool) { 122 for i, j := from, 0; i <= to; i, j = i+1, j+1 { 123 checkHistory(t, db, freezer, i, roots[j], exist) 124 } 125 } 126 127 func TestTruncateHeadHistory(t *testing.T) { 128 var ( 129 roots []common.Hash 130 hs = makeHistories(10) 131 db = rawdb.NewMemoryDatabase() 132 freezer, _ = openFreezer(t.TempDir(), false) 133 ) 134 defer freezer.Close() 135 136 for i := 0; i < len(hs); i++ { 137 accountData, storageData, accountIndex, storageIndex := hs[i].encode() 138 rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData) 139 rawdb.WriteStateID(db, hs[i].meta.root, uint64(i+1)) 140 roots = append(roots, hs[i].meta.root) 141 } 142 for size := len(hs); size > 0; size-- { 143 pruned, err := truncateFromHead(db, freezer, uint64(size-1)) 144 if err != nil { 145 t.Fatalf("Failed to truncate from head %v", err) 146 } 147 if pruned != 1 { 148 t.Error("Unexpected pruned items", "want", 1, "got", pruned) 149 } 150 checkHistoriesInRange(t, db, freezer, uint64(size), uint64(10), roots[size-1:], false) 151 checkHistoriesInRange(t, db, freezer, uint64(1), uint64(size-1), roots[:size-1], true) 152 } 153 } 154 155 func TestTruncateTailHistory(t *testing.T) { 156 var ( 157 roots []common.Hash 158 hs = makeHistories(10) 159 db = rawdb.NewMemoryDatabase() 160 freezer, _ = openFreezer(t.TempDir(), false) 161 ) 162 defer freezer.Close() 163 164 for i := 0; i < len(hs); i++ { 165 accountData, storageData, accountIndex, storageIndex := hs[i].encode() 166 rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData) 167 rawdb.WriteStateID(db, hs[i].meta.root, uint64(i+1)) 168 roots = append(roots, hs[i].meta.root) 169 } 170 for newTail := 1; newTail < len(hs); newTail++ { 171 pruned, _ := truncateFromTail(db, freezer, uint64(newTail)) 172 if pruned != 1 { 173 t.Error("Unexpected pruned items", "want", 1, "got", pruned) 174 } 175 checkHistoriesInRange(t, db, freezer, uint64(1), uint64(newTail), roots[:newTail], false) 176 checkHistoriesInRange(t, db, freezer, uint64(newTail+1), uint64(10), roots[newTail:], true) 177 } 178 } 179 180 func TestTruncateTailHistories(t *testing.T) { 181 var cases = []struct { 182 limit uint64 183 expPruned int 184 maxPruned uint64 185 minUnpruned uint64 186 empty bool 187 }{ 188 { 189 1, 9, 9, 10, false, 190 }, 191 { 192 0, 10, 10, 0 /* no meaning */, true, 193 }, 194 { 195 10, 0, 0, 1, false, 196 }, 197 } 198 for i, c := range cases { 199 var ( 200 roots []common.Hash 201 hs = makeHistories(10) 202 db = rawdb.NewMemoryDatabase() 203 freezer, _ = openFreezer(t.TempDir()+fmt.Sprintf("%d", i), false) 204 ) 205 defer freezer.Close() 206 207 for i := 0; i < len(hs); i++ { 208 accountData, storageData, accountIndex, storageIndex := hs[i].encode() 209 rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData) 210 rawdb.WriteStateID(db, hs[i].meta.root, uint64(i+1)) 211 roots = append(roots, hs[i].meta.root) 212 } 213 pruned, _ := truncateFromTail(db, freezer, uint64(10)-c.limit) 214 if pruned != c.expPruned { 215 t.Error("Unexpected pruned items", "want", c.expPruned, "got", pruned) 216 } 217 if c.empty { 218 checkHistoriesInRange(t, db, freezer, uint64(1), uint64(10), roots, false) 219 } else { 220 tail := 10 - int(c.limit) 221 checkHistoriesInRange(t, db, freezer, uint64(1), c.maxPruned, roots[:tail], false) 222 checkHistoriesInRange(t, db, freezer, c.minUnpruned, uint64(10), roots[tail:], true) 223 } 224 } 225 } 226 227 // openFreezer initializes the freezer instance for storing state histories. 228 func openFreezer(datadir string, readOnly bool) (*rawdb.ResettableFreezer, error) { 229 return rawdb.NewStateFreezer(datadir, readOnly) 230 } 231 232 func compareSet[k comparable](a, b map[k][]byte) bool { 233 if len(a) != len(b) { 234 return false 235 } 236 for key, valA := range a { 237 valB, ok := b[key] 238 if !ok { 239 return false 240 } 241 if !bytes.Equal(valA, valB) { 242 return false 243 } 244 } 245 return true 246 } 247 248 func compareList[k comparable](a, b []k) bool { 249 if len(a) != len(b) { 250 return false 251 } 252 for i := 0; i < len(a); i++ { 253 if a[i] != b[i] { 254 return false 255 } 256 } 257 return true 258 } 259 260 func compareStorages(a, b map[common.Address]map[common.Hash][]byte) bool { 261 if len(a) != len(b) { 262 return false 263 } 264 for h, subA := range a { 265 subB, ok := b[h] 266 if !ok { 267 return false 268 } 269 if !compareSet(subA, subB) { 270 return false 271 } 272 } 273 return true 274 } 275 276 func compareStorageList(a, b map[common.Address][]common.Hash) bool { 277 if len(a) != len(b) { 278 return false 279 } 280 for h, la := range a { 281 lb, ok := b[h] 282 if !ok { 283 return false 284 } 285 if !compareList(la, lb) { 286 return false 287 } 288 } 289 return true 290 }