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