github.com/hardtosaygoodbye/go-ethereum@v1.10.16-0.20220122011429-97003b9e6c15/les/pruner_test.go (about) 1 // Copyright 2019 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 les 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/binary" 23 "testing" 24 "time" 25 26 "github.com/hardtosaygoodbye/go-ethereum/core" 27 "github.com/hardtosaygoodbye/go-ethereum/light" 28 ) 29 30 func TestLightPruner(t *testing.T) { 31 var ( 32 waitIndexers = func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) { 33 for { 34 cs, _, _ := cIndexer.Sections() 35 bts, _, _ := btIndexer.Sections() 36 if cs >= 3 && bts >= 3 { 37 break 38 } 39 time.Sleep(10 * time.Millisecond) 40 } 41 } 42 config = light.TestClientIndexerConfig 43 netconfig = testnetConfig{ 44 blocks: int(3*config.ChtSize + config.ChtConfirms), 45 protocol: 3, 46 indexFn: waitIndexers, 47 connect: true, 48 } 49 ) 50 server, client, tearDown := newClientServerEnv(t, netconfig) 51 defer tearDown() 52 53 // checkDB iterates the chain with given prefix, resolves the block number 54 // with given callback and ensures this entry should exist or not. 55 checkDB := func(from, to uint64, prefix []byte, resolve func(key, value []byte) *uint64, exist bool) bool { 56 it := client.db.NewIterator(prefix, nil) 57 defer it.Release() 58 59 var next = from 60 for it.Next() { 61 number := resolve(it.Key(), it.Value()) 62 if number == nil || *number < from { 63 continue 64 } else if *number > to { 65 return true 66 } 67 if exist { 68 if *number != next { 69 return false 70 } 71 next++ 72 } else { 73 return false 74 } 75 } 76 return true 77 } 78 // checkPruned checks and ensures the stale chain data has been pruned. 79 checkPruned := func(from, to uint64) { 80 // Iterate canonical hash 81 if !checkDB(from, to, []byte("h"), func(key, value []byte) *uint64 { 82 if len(key) == 1+8+1 && bytes.Equal(key[9:10], []byte("n")) { 83 n := binary.BigEndian.Uint64(key[1:9]) 84 return &n 85 } 86 return nil 87 }, false) { 88 t.Fatalf("canonical hash mappings are not properly pruned") 89 } 90 // Iterate header 91 if !checkDB(from, to, []byte("h"), func(key, value []byte) *uint64 { 92 if len(key) == 1+8+32 { 93 n := binary.BigEndian.Uint64(key[1:9]) 94 return &n 95 } 96 return nil 97 }, false) { 98 t.Fatalf("headers are not properly pruned") 99 } 100 // Iterate body 101 if !checkDB(from, to, []byte("b"), func(key, value []byte) *uint64 { 102 if len(key) == 1+8+32 { 103 n := binary.BigEndian.Uint64(key[1:9]) 104 return &n 105 } 106 return nil 107 }, false) { 108 t.Fatalf("block bodies are not properly pruned") 109 } 110 // Iterate receipts 111 if !checkDB(from, to, []byte("r"), func(key, value []byte) *uint64 { 112 if len(key) == 1+8+32 { 113 n := binary.BigEndian.Uint64(key[1:9]) 114 return &n 115 } 116 return nil 117 }, false) { 118 t.Fatalf("receipts are not properly pruned") 119 } 120 // Iterate td 121 if !checkDB(from, to, []byte("h"), func(key, value []byte) *uint64 { 122 if len(key) == 1+8+32+1 && bytes.Equal(key[41:42], []byte("t")) { 123 n := binary.BigEndian.Uint64(key[1:9]) 124 return &n 125 } 126 return nil 127 }, false) { 128 t.Fatalf("tds are not properly pruned") 129 } 130 } 131 // Start light pruner. 132 time.Sleep(1500 * time.Millisecond) // Ensure light client has finished the syncing and indexing 133 newPruner(client.db, client.chtIndexer, client.bloomTrieIndexer) 134 135 time.Sleep(1500 * time.Millisecond) // Ensure pruner have enough time to prune data. 136 checkPruned(1, config.ChtSize-1) 137 138 // Ensure all APIs still work after pruning. 139 var cases = []struct { 140 from, to uint64 141 methodName string 142 method func(uint64) bool 143 }{ 144 { 145 1, 10, "GetHeaderByNumber", 146 func(n uint64) bool { 147 _, err := light.GetHeaderByNumber(context.Background(), client.handler.backend.odr, n) 148 return err == nil 149 }, 150 }, 151 { 152 11, 20, "GetCanonicalHash", 153 func(n uint64) bool { 154 _, err := light.GetCanonicalHash(context.Background(), client.handler.backend.odr, n) 155 return err == nil 156 }, 157 }, 158 { 159 21, 30, "GetTd", 160 func(n uint64) bool { 161 _, err := light.GetTd(context.Background(), client.handler.backend.odr, server.handler.blockchain.GetHeaderByNumber(n).Hash(), n) 162 return err == nil 163 }, 164 }, 165 { 166 31, 40, "GetBodyRLP", 167 func(n uint64) bool { 168 _, err := light.GetBodyRLP(context.Background(), client.handler.backend.odr, server.handler.blockchain.GetHeaderByNumber(n).Hash(), n) 169 return err == nil 170 }, 171 }, 172 { 173 41, 50, "GetBlock", 174 func(n uint64) bool { 175 _, err := light.GetBlock(context.Background(), client.handler.backend.odr, server.handler.blockchain.GetHeaderByNumber(n).Hash(), n) 176 return err == nil 177 }, 178 }, 179 { 180 51, 60, "GetBlockReceipts", 181 func(n uint64) bool { 182 _, err := light.GetBlockReceipts(context.Background(), client.handler.backend.odr, server.handler.blockchain.GetHeaderByNumber(n).Hash(), n) 183 return err == nil 184 }, 185 }, 186 } 187 for _, c := range cases { 188 for i := c.from; i <= c.to; i++ { 189 if !c.method(i) { 190 t.Fatalf("rpc method %s failed, number %d", c.methodName, i) 191 } 192 } 193 } 194 // Check GetBloombits 195 _, err := light.GetBloomBits(context.Background(), client.handler.backend.odr, 0, []uint64{0}) 196 if err != nil { 197 t.Fatalf("Failed to retrieve bloombits of pruned section: %v", err) 198 } 199 200 // Ensure the ODR cached data can be cleaned by pruner. 201 newPruner(client.db, client.chtIndexer, client.bloomTrieIndexer) 202 time.Sleep(50 * time.Millisecond) // Ensure pruner have enough time to prune data. 203 checkPruned(1, config.ChtSize-1) // Ensure all cached data(by odr) is cleaned. 204 }