github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/core/rawdb" 27 "github.com/ethereum/go-ethereum/core/types" 28 "github.com/ethereum/go-ethereum/ethdb" 29 "github.com/ethereum/go-ethereum/internal/testrand" 30 "github.com/ethereum/go-ethereum/rlp" 31 "github.com/ethereum/go-ethereum/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 := testrand.Address() 42 storages[addr] = make(map[common.Hash][]byte) 43 for j := 0; j < 3; j++ { 44 v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testrand.Bytes(32))) 45 storages[addr][testrand.Hash()] = v 46 } 47 account := generateAccount(types.EmptyRootHash) 48 accounts[addr] = types.SlimAccountRLP(account) 49 } 50 return triestate.New(accounts, storages) 51 } 52 53 func makeHistory() *history { 54 return newHistory(testrand.Hash(), 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 := testrand.Hash() 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 ethdb.KeyValueReader, freezer ethdb.AncientReader, 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 ethdb.KeyValueReader, freezer ethdb.AncientReader, 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, _ = rawdb.NewStateFreezer(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, _ = rawdb.NewStateFreezer(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, _ = rawdb.NewStateFreezer(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 func TestTruncateOutOfRange(t *testing.T) { 228 var ( 229 hs = makeHistories(10) 230 db = rawdb.NewMemoryDatabase() 231 freezer, _ = rawdb.NewStateFreezer(t.TempDir(), false) 232 ) 233 defer freezer.Close() 234 235 for i := 0; i < len(hs); i++ { 236 accountData, storageData, accountIndex, storageIndex := hs[i].encode() 237 rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData) 238 rawdb.WriteStateID(db, hs[i].meta.root, uint64(i+1)) 239 } 240 truncateFromTail(db, freezer, uint64(len(hs)/2)) 241 242 // Ensure of-out-range truncations are rejected correctly. 243 head, _ := freezer.Ancients() 244 tail, _ := freezer.Tail() 245 246 cases := []struct { 247 mode int 248 target uint64 249 expErr error 250 }{ 251 {0, head, nil}, // nothing to delete 252 {0, head + 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, head+1)}, 253 {0, tail - 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, tail-1)}, 254 {1, tail, nil}, // nothing to delete 255 {1, head + 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, head+1)}, 256 {1, tail - 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, tail-1)}, 257 } 258 for _, c := range cases { 259 var gotErr error 260 if c.mode == 0 { 261 _, gotErr = truncateFromHead(db, freezer, c.target) 262 } else { 263 _, gotErr = truncateFromTail(db, freezer, c.target) 264 } 265 if !reflect.DeepEqual(gotErr, c.expErr) { 266 t.Errorf("Unexpected error, want: %v, got: %v", c.expErr, gotErr) 267 } 268 } 269 } 270 271 func compareSet[k comparable](a, b map[k][]byte) bool { 272 if len(a) != len(b) { 273 return false 274 } 275 for key, valA := range a { 276 valB, ok := b[key] 277 if !ok { 278 return false 279 } 280 if !bytes.Equal(valA, valB) { 281 return false 282 } 283 } 284 return true 285 } 286 287 func compareList[k comparable](a, b []k) bool { 288 if len(a) != len(b) { 289 return false 290 } 291 for i := 0; i < len(a); i++ { 292 if a[i] != b[i] { 293 return false 294 } 295 } 296 return true 297 } 298 299 func compareStorages(a, b map[common.Address]map[common.Hash][]byte) bool { 300 if len(a) != len(b) { 301 return false 302 } 303 for h, subA := range a { 304 subB, ok := b[h] 305 if !ok { 306 return false 307 } 308 if !compareSet(subA, subB) { 309 return false 310 } 311 } 312 return true 313 } 314 315 func compareStorageList(a, b map[common.Address][]common.Hash) bool { 316 if len(a) != len(b) { 317 return false 318 } 319 for h, la := range a { 320 lb, ok := b[h] 321 if !ok { 322 return false 323 } 324 if !compareList(la, lb) { 325 return false 326 } 327 } 328 return true 329 }