github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/trie/sync_test.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:45</date> 10 //</624450123509796864> 11 12 13 package trie 14 15 import ( 16 "bytes" 17 "testing" 18 19 "github.com/ethereum/go-ethereum/common" 20 "github.com/ethereum/go-ethereum/ethdb" 21 ) 22 23 //maketesttrie创建一个样本test trie来测试节点重建。 24 func makeTestTrie() (*Database, *Trie, map[string][]byte) { 25 //创建空的trie 26 triedb := NewDatabase(ethdb.NewMemDatabase()) 27 trie, _ := New(common.Hash{}, triedb) 28 29 //用任意数据填充它 30 content := make(map[string][]byte) 31 for i := byte(0); i < 255; i++ { 32 //在多个键下映射相同的数据 33 key, val := common.LeftPadBytes([]byte{1, i}, 32), []byte{i} 34 content[string(key)] = val 35 trie.Update(key, val) 36 37 key, val = common.LeftPadBytes([]byte{2, i}, 32), []byte{i} 38 content[string(key)] = val 39 trie.Update(key, val) 40 41 //添加一些其他数据来填充trie 42 for j := byte(3); j < 13; j++ { 43 key, val = common.LeftPadBytes([]byte{j, i}, 32), []byte{j, i} 44 content[string(key)] = val 45 trie.Update(key, val) 46 } 47 } 48 trie.Commit(nil) 49 50 //返回生成的trie 51 return triedb, trie, content 52 } 53 54 //checktrieContents使用预期数据交叉引用重构的trie 55 //内容图。 56 func checkTrieContents(t *testing.T, db *Database, root []byte, content map[string][]byte) { 57 //检查根可用性和trie内容 58 trie, err := New(common.BytesToHash(root), db) 59 if err != nil { 60 t.Fatalf("failed to create trie at %x: %v", root, err) 61 } 62 if err := checkTrieConsistency(db, common.BytesToHash(root)); err != nil { 63 t.Fatalf("inconsistent trie at %x: %v", root, err) 64 } 65 for key, val := range content { 66 if have := trie.Get([]byte(key)); !bytes.Equal(have, val) { 67 t.Errorf("entry %x: content mismatch: have %x, want %x", key, have, val) 68 } 69 } 70 } 71 72 //checktrieconsibility检查trie中的所有节点是否确实存在。 73 func checkTrieConsistency(db *Database, root common.Hash) error { 74 //创建并迭代子节点中的trie 75 trie, err := New(root, db) 76 if err != nil { 77 return nil //认为不存在的状态是一致的 78 } 79 it := trie.NodeIterator(nil) 80 for it.Next(true) { 81 } 82 return it.Error() 83 } 84 85 //测试空的trie没有计划同步。 86 func TestEmptySync(t *testing.T) { 87 dbA := NewDatabase(ethdb.NewMemDatabase()) 88 dbB := NewDatabase(ethdb.NewMemDatabase()) 89 emptyA, _ := New(common.Hash{}, dbA) 90 emptyB, _ := New(emptyRoot, dbB) 91 92 for i, trie := range []*Trie{emptyA, emptyB} { 93 if req := NewSync(trie.Hash(), ethdb.NewMemDatabase(), nil).Missing(1); len(req) != 0 { 94 t.Errorf("test %d: content requested for empty trie: %v", i, req) 95 } 96 } 97 } 98 99 //测试给定根哈希,trie可以在单个线程上迭代同步, 100 //请求检索任务并一次性返回所有任务。 101 func TestIterativeSyncIndividual(t *testing.T) { testIterativeSync(t, 1) } 102 func TestIterativeSyncBatched(t *testing.T) { testIterativeSync(t, 100) } 103 104 func testIterativeSync(t *testing.T, batch int) { 105 //创建要复制的随机trie 106 srcDb, srcTrie, srcData := makeTestTrie() 107 108 //创建目标trie并与调度程序同步 109 diskdb := ethdb.NewMemDatabase() 110 triedb := NewDatabase(diskdb) 111 sched := NewSync(srcTrie.Hash(), diskdb, nil) 112 113 queue := append([]common.Hash{}, sched.Missing(batch)...) 114 for len(queue) > 0 { 115 results := make([]SyncResult, len(queue)) 116 for i, hash := range queue { 117 data, err := srcDb.Node(hash) 118 if err != nil { 119 t.Fatalf("failed to retrieve node data for %x: %v", hash, err) 120 } 121 results[i] = SyncResult{hash, data} 122 } 123 if _, index, err := sched.Process(results); err != nil { 124 t.Fatalf("failed to process result #%d: %v", index, err) 125 } 126 if index, err := sched.Commit(diskdb); err != nil { 127 t.Fatalf("failed to commit data #%d: %v", index, err) 128 } 129 queue = append(queue[:0], sched.Missing(batch)...) 130 } 131 //交叉检查两次尝试是否同步 132 checkTrieContents(t, triedb, srcTrie.Root(), srcData) 133 } 134 135 //测试trie调度程序是否可以正确地重建状态,即使只有 136 //返回部分结果,其他结果只在稍后发送。 137 func TestIterativeDelayedSync(t *testing.T) { 138 //创建要复制的随机trie 139 srcDb, srcTrie, srcData := makeTestTrie() 140 141 //创建目标trie并与调度程序同步 142 diskdb := ethdb.NewMemDatabase() 143 triedb := NewDatabase(diskdb) 144 sched := NewSync(srcTrie.Hash(), diskdb, nil) 145 146 queue := append([]common.Hash{}, sched.Missing(10000)...) 147 for len(queue) > 0 { 148 //只同步一半的计划节点 149 results := make([]SyncResult, len(queue)/2+1) 150 for i, hash := range queue[:len(results)] { 151 data, err := srcDb.Node(hash) 152 if err != nil { 153 t.Fatalf("failed to retrieve node data for %x: %v", hash, err) 154 } 155 results[i] = SyncResult{hash, data} 156 } 157 if _, index, err := sched.Process(results); err != nil { 158 t.Fatalf("failed to process result #%d: %v", index, err) 159 } 160 if index, err := sched.Commit(diskdb); err != nil { 161 t.Fatalf("failed to commit data #%d: %v", index, err) 162 } 163 queue = append(queue[len(results):], sched.Missing(10000)...) 164 } 165 //交叉检查两次尝试是否同步 166 checkTrieContents(t, triedb, srcTrie.Root(), srcData) 167 } 168 169 //测试给定根哈希,trie可以在单个线程上迭代同步, 170 //请求检索任务并一次性返回所有任务,但是在 171 //随机顺序。 172 func TestIterativeRandomSyncIndividual(t *testing.T) { testIterativeRandomSync(t, 1) } 173 func TestIterativeRandomSyncBatched(t *testing.T) { testIterativeRandomSync(t, 100) } 174 175 func testIterativeRandomSync(t *testing.T, batch int) { 176 //创建要复制的随机trie 177 srcDb, srcTrie, srcData := makeTestTrie() 178 179 //创建目标trie并与调度程序同步 180 diskdb := ethdb.NewMemDatabase() 181 triedb := NewDatabase(diskdb) 182 sched := NewSync(srcTrie.Hash(), diskdb, nil) 183 184 queue := make(map[common.Hash]struct{}) 185 for _, hash := range sched.Missing(batch) { 186 queue[hash] = struct{}{} 187 } 188 for len(queue) > 0 { 189 //以随机顺序获取所有排队的节点 190 results := make([]SyncResult, 0, len(queue)) 191 for hash := range queue { 192 data, err := srcDb.Node(hash) 193 if err != nil { 194 t.Fatalf("failed to retrieve node data for %x: %v", hash, err) 195 } 196 results = append(results, SyncResult{hash, data}) 197 } 198 //将检索到的结果反馈并将新任务排队 199 if _, index, err := sched.Process(results); err != nil { 200 t.Fatalf("failed to process result #%d: %v", index, err) 201 } 202 if index, err := sched.Commit(diskdb); err != nil { 203 t.Fatalf("failed to commit data #%d: %v", index, err) 204 } 205 queue = make(map[common.Hash]struct{}) 206 for _, hash := range sched.Missing(batch) { 207 queue[hash] = struct{}{} 208 } 209 } 210 //交叉检查两次尝试是否同步 211 checkTrieContents(t, triedb, srcTrie.Root(), srcData) 212 } 213 214 //测试trie调度程序是否可以正确地重建状态,即使只有 215 //部分结果会被返回(甚至是随机返回的结果),其他结果只会在稍后发送。 216 func TestIterativeRandomDelayedSync(t *testing.T) { 217 //创建要复制的随机trie 218 srcDb, srcTrie, srcData := makeTestTrie() 219 220 //创建目标trie并与调度程序同步 221 diskdb := ethdb.NewMemDatabase() 222 triedb := NewDatabase(diskdb) 223 sched := NewSync(srcTrie.Hash(), diskdb, nil) 224 225 queue := make(map[common.Hash]struct{}) 226 for _, hash := range sched.Missing(10000) { 227 queue[hash] = struct{}{} 228 } 229 for len(queue) > 0 { 230 //只同步一半的计划节点,甚至是随机顺序的节点 231 results := make([]SyncResult, 0, len(queue)/2+1) 232 for hash := range queue { 233 data, err := srcDb.Node(hash) 234 if err != nil { 235 t.Fatalf("failed to retrieve node data for %x: %v", hash, err) 236 } 237 results = append(results, SyncResult{hash, data}) 238 239 if len(results) >= cap(results) { 240 break 241 } 242 } 243 //将检索到的结果反馈并将新任务排队 244 if _, index, err := sched.Process(results); err != nil { 245 t.Fatalf("failed to process result #%d: %v", index, err) 246 } 247 if index, err := sched.Commit(diskdb); err != nil { 248 t.Fatalf("failed to commit data #%d: %v", index, err) 249 } 250 for _, result := range results { 251 delete(queue, result.Hash) 252 } 253 for _, hash := range sched.Missing(10000) { 254 queue[hash] = struct{}{} 255 } 256 } 257 //交叉检查两次尝试是否同步 258 checkTrieContents(t, triedb, srcTrie.Root(), srcData) 259 } 260 261 //测试trie-sync不会多次请求节点,即使它们 262 //有这样的证明人。 263 func TestDuplicateAvoidanceSync(t *testing.T) { 264 //创建要复制的随机trie 265 srcDb, srcTrie, srcData := makeTestTrie() 266 267 //创建目标trie并与调度程序同步 268 diskdb := ethdb.NewMemDatabase() 269 triedb := NewDatabase(diskdb) 270 sched := NewSync(srcTrie.Hash(), diskdb, nil) 271 272 queue := append([]common.Hash{}, sched.Missing(0)...) 273 requested := make(map[common.Hash]struct{}) 274 275 for len(queue) > 0 { 276 results := make([]SyncResult, len(queue)) 277 for i, hash := range queue { 278 data, err := srcDb.Node(hash) 279 if err != nil { 280 t.Fatalf("failed to retrieve node data for %x: %v", hash, err) 281 } 282 if _, ok := requested[hash]; ok { 283 t.Errorf("hash %x already requested once", hash) 284 } 285 requested[hash] = struct{}{} 286 287 results[i] = SyncResult{hash, data} 288 } 289 if _, index, err := sched.Process(results); err != nil { 290 t.Fatalf("failed to process result #%d: %v", index, err) 291 } 292 if index, err := sched.Commit(diskdb); err != nil { 293 t.Fatalf("failed to commit data #%d: %v", index, err) 294 } 295 queue = append(queue[:0], sched.Missing(0)...) 296 } 297 //交叉检查两次尝试是否同步 298 checkTrieContents(t, triedb, srcTrie.Root(), srcData) 299 } 300 301 //测试在同步过程中的任何时间点,只有完整的子尝试处于 302 //数据库。 303 func TestIncompleteSync(t *testing.T) { 304 //创建要复制的随机trie 305 srcDb, srcTrie, _ := makeTestTrie() 306 307 //创建目标trie并与调度程序同步 308 diskdb := ethdb.NewMemDatabase() 309 triedb := NewDatabase(diskdb) 310 sched := NewSync(srcTrie.Hash(), diskdb, nil) 311 312 added := []common.Hash{} 313 queue := append([]common.Hash{}, sched.Missing(1)...) 314 for len(queue) > 0 { 315 //获取一批trie节点 316 results := make([]SyncResult, len(queue)) 317 for i, hash := range queue { 318 data, err := srcDb.Node(hash) 319 if err != nil { 320 t.Fatalf("failed to retrieve node data for %x: %v", hash, err) 321 } 322 results[i] = SyncResult{hash, data} 323 } 324 //处理每个trie节点 325 if _, index, err := sched.Process(results); err != nil { 326 t.Fatalf("failed to process result #%d: %v", index, err) 327 } 328 if index, err := sched.Commit(diskdb); err != nil { 329 t.Fatalf("failed to commit data #%d: %v", index, err) 330 } 331 for _, result := range results { 332 added = append(added, result.Hash) 333 } 334 //检查同步trie中的所有已知子尝试是否完成 335 for _, root := range added { 336 if err := checkTrieConsistency(triedb, root); err != nil { 337 t.Fatalf("trie inconsistent: %v", err) 338 } 339 } 340 //获取要检索的下一批 341 queue = append(queue[:0], sched.Missing(1)...) 342 } 343 //健全性检查是否检测到从数据库中删除任何节点 344 for _, node := range added[1:] { 345 key := node.Bytes() 346 value, _ := diskdb.Get(key) 347 348 diskdb.Delete(key) 349 if err := checkTrieConsistency(triedb, added[0]); err == nil { 350 t.Fatalf("trie inconsistency not caught, missing: %x", key) 351 } 352 diskdb.Put(key, value) 353 } 354 } 355