github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/trie/sync.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 //</624450123451076608> 11 12 13 package trie 14 15 import ( 16 "errors" 17 "fmt" 18 19 "github.com/ethereum/go-ethereum/common" 20 "github.com/ethereum/go-ethereum/common/prque" 21 "github.com/ethereum/go-ethereum/ethdb" 22 ) 23 24 //当请求trie-sync处理 25 //它没有请求的节点。 26 var ErrNotRequested = errors.New("not requested") 27 28 //当请求trie-sync处理 29 //它以前已经处理过的节点。 30 var ErrAlreadyProcessed = errors.New("already processed") 31 32 //请求表示预定的或已在飞行中的状态检索请求。 33 type request struct { 34 hash common.Hash //要检索的节点数据内容的哈希 35 data []byte //节点的数据内容,缓存到所有子树完成 36 raw bool //这是原始条目(代码)还是trie节点 37 38 parents []*request //引用此条目的父状态节点(完成时通知所有节点) 39 depth int //Trie中的深度级别节点的位置可优先考虑DFS 40 deps int //允许提交此节点之前的依赖项数 41 42 callback LeafCallback //调用此分支上的叶节点 43 } 44 45 //SyncResult是一个简单的列表,用于返回丢失的节点及其请求 46 //散列。 47 type SyncResult struct { 48 Hash common.Hash //原始未知trie节点的哈希 49 Data []byte //检索节点的数据内容 50 } 51 52 //SyncMembatch是成功下载但尚未下载的内存缓冲区。 53 //保留的数据项。 54 type syncMemBatch struct { 55 batch map[common.Hash][]byte //内存中最近完成的项的membatch 56 order []common.Hash //完成订单以防止无序数据丢失 57 } 58 59 //newsyncmembatch为尚未持久化的trie节点分配新的内存缓冲区。 60 func newSyncMemBatch() *syncMemBatch { 61 return &syncMemBatch{ 62 batch: make(map[common.Hash][]byte), 63 order: make([]common.Hash, 0, 256), 64 } 65 } 66 67 //同步是主要的状态三同步调度程序,它提供了 68 //要检索的trie哈希未知,接受与所述哈希关联的节点数据 69 //一步一步地重建Trie,直到全部完成。 70 type Sync struct { 71 database DatabaseReader //用于检查现有条目的持久数据库 72 membatch *syncMemBatch //内存缓冲区以避免频繁的数据库写入 73 requests map[common.Hash]*request //与密钥哈希相关的挂起请求 74 queue *prque.Prque //具有挂起请求的优先级队列 75 } 76 77 //newsync创建一个新的trie数据下载调度程序。 78 func NewSync(root common.Hash, database DatabaseReader, callback LeafCallback) *Sync { 79 ts := &Sync{ 80 database: database, 81 membatch: newSyncMemBatch(), 82 requests: make(map[common.Hash]*request), 83 queue: prque.New(nil), 84 } 85 ts.AddSubTrie(root, 0, common.Hash{}, callback) 86 return ts 87 } 88 89 //addsubrie将一个新的trie注册到同步代码,根位于指定的父级。 90 func (s *Sync) AddSubTrie(root common.Hash, depth int, parent common.Hash, callback LeafCallback) { 91 //如果Trie为空或已知,则短路 92 if root == emptyRoot { 93 return 94 } 95 if _, ok := s.membatch.batch[root]; ok { 96 return 97 } 98 key := root.Bytes() 99 blob, _ := s.database.Get(key) 100 if local, err := decodeNode(key, blob, 0); local != nil && err == nil { 101 return 102 } 103 //组装新的Sub-Trie同步请求 104 req := &request{ 105 hash: root, 106 depth: depth, 107 callback: callback, 108 } 109 //如果此子目录有指定的父目录,请将它们链接在一起 110 if parent != (common.Hash{}) { 111 ancestor := s.requests[parent] 112 if ancestor == nil { 113 panic(fmt.Sprintf("sub-trie ancestor not found: %x", parent)) 114 } 115 ancestor.deps++ 116 req.parents = append(req.parents, ancestor) 117 } 118 s.schedule(req) 119 } 120 121 //addrawentry计划直接检索不应 122 //解释为trie节点,但被接受并存储到数据库中 123 //事实也是如此。此方法的目标是支持各种状态元数据检索(例如 124 //合同代码)。 125 func (s *Sync) AddRawEntry(hash common.Hash, depth int, parent common.Hash) { 126 //如果条目为空或已知,则短路 127 if hash == emptyState { 128 return 129 } 130 if _, ok := s.membatch.batch[hash]; ok { 131 return 132 } 133 if ok, _ := s.database.Has(hash.Bytes()); ok { 134 return 135 } 136 //组装新的Sub-Trie同步请求 137 req := &request{ 138 hash: hash, 139 raw: true, 140 depth: depth, 141 } 142 //如果此子目录有指定的父目录,请将它们链接在一起 143 if parent != (common.Hash{}) { 144 ancestor := s.requests[parent] 145 if ancestor == nil { 146 panic(fmt.Sprintf("raw-entry ancestor not found: %x", parent)) 147 } 148 ancestor.deps++ 149 req.parents = append(req.parents, ancestor) 150 } 151 s.schedule(req) 152 } 153 154 //Missing从trie中检索已知的丢失节点以进行检索。 155 func (s *Sync) Missing(max int) []common.Hash { 156 requests := []common.Hash{} 157 for !s.queue.Empty() && (max == 0 || len(requests) < max) { 158 requests = append(requests, s.queue.PopItem().(common.Hash)) 159 } 160 return requests 161 } 162 163 //进程注入一批检索到的trie节点数据,如果有什么返回 164 //已提交到数据库,如果处理 165 //它失败了。 166 func (s *Sync) Process(results []SyncResult) (bool, int, error) { 167 committed := false 168 169 for i, item := range results { 170 //如果该项目没有被要求,请退出。 171 request := s.requests[item.Hash] 172 if request == nil { 173 return committed, i, ErrNotRequested 174 } 175 if request.data != nil { 176 return committed, i, ErrAlreadyProcessed 177 } 178 //如果该项是原始输入请求,则直接提交 179 if request.raw { 180 request.data = item.Data 181 s.commit(request) 182 committed = true 183 continue 184 } 185 //解码节点数据内容并更新请求 186 node, err := decodeNode(item.Hash[:], item.Data, 0) 187 if err != nil { 188 return committed, i, err 189 } 190 request.data = item.Data 191 192 //为所有子节点创建和调度请求 193 requests, err := s.children(request, node) 194 if err != nil { 195 return committed, i, err 196 } 197 if len(requests) == 0 && request.deps == 0 { 198 s.commit(request) 199 committed = true 200 continue 201 } 202 request.deps += len(requests) 203 for _, child := range requests { 204 s.schedule(child) 205 } 206 } 207 return committed, 0, nil 208 } 209 210 //commit将存储在内部membatch中的数据刷新为persistent 211 //存储,返回写入的项目数和发生的任何错误。 212 func (s *Sync) Commit(dbw ethdb.Putter) (int, error) { 213 //将membatch转储到数据库dbw中 214 for i, key := range s.membatch.order { 215 if err := dbw.Put(key[:], s.membatch.batch[key]); err != nil { 216 return i, err 217 } 218 } 219 written := len(s.membatch.order) 220 221 //删除membatch数据并返回 222 s.membatch = newSyncMemBatch() 223 return written, nil 224 } 225 226 //Pending返回当前等待下载的状态条目数。 227 func (s *Sync) Pending() int { 228 return len(s.requests) 229 } 230 231 //计划将新的状态检索请求插入提取队列。如果有 232 //已经是此节点的挂起请求,新请求将被丢弃。 233 //只有一个父引用添加到旧的引用中。 234 func (s *Sync) schedule(req *request) { 235 //如果我们已经请求这个节点,添加一个新的引用并停止 236 if old, ok := s.requests[req.hash]; ok { 237 old.parents = append(old.parents, req.parents...) 238 return 239 } 240 //为以后的检索安排请求 241 s.queue.Push(req.hash, int64(req.depth)) 242 s.requests[req.hash] = req 243 } 244 245 //children为将来检索一个state trie项中所有丢失的子项 246 //检索调度。 247 func (s *Sync) children(req *request, object node) ([]*request, error) { 248 //收集节点的所有子节点,无论是否已知,都不相关 249 type child struct { 250 node node 251 depth int 252 } 253 children := []child{} 254 255 switch node := (object).(type) { 256 case *shortNode: 257 children = []child{{ 258 node: node.Val, 259 depth: req.depth + len(node.Key), 260 }} 261 case *fullNode: 262 for i := 0; i < 17; i++ { 263 if node.Children[i] != nil { 264 children = append(children, child{ 265 node: node.Children[i], 266 depth: req.depth + 1, 267 }) 268 } 269 } 270 default: 271 panic(fmt.Sprintf("unknown node: %+v", node)) 272 } 273 //循环访问子级,并请求所有未知的子级 274 requests := make([]*request, 0, len(children)) 275 for _, child := range children { 276 //通知任何外部观察程序新的键/值节点 277 if req.callback != nil { 278 if node, ok := (child.node).(valueNode); ok { 279 if err := req.callback(node, req.hash); err != nil { 280 return nil, err 281 } 282 } 283 } 284 //如果子节点引用另一个节点,请解析或调度 285 if node, ok := (child.node).(hashNode); ok { 286 //尝试从本地数据库解析节点 287 hash := common.BytesToHash(node) 288 if _, ok := s.membatch.batch[hash]; ok { 289 continue 290 } 291 if ok, _ := s.database.Has(node); ok { 292 continue 293 } 294 //本地未知节点,检索计划 295 requests = append(requests, &request{ 296 hash: hash, 297 parents: []*request{req}, 298 depth: child.depth, 299 callback: req.callback, 300 }) 301 } 302 } 303 return requests, nil 304 } 305 306 //提交完成检索请求并将其存储到membatch中。如果有的话 307 //在由于这个提交而完成的引用父请求中,它们也是 308 //承诺自己。 309 func (s *Sync) commit(req *request) (err error) { 310 //将节点内容写入membatch 311 s.membatch.batch[req.hash] = req.data 312 s.membatch.order = append(s.membatch.order, req.hash) 313 314 delete(s.requests, req.hash) 315 316 //检查所有家长是否完成 317 for _, parent := range req.parents { 318 parent.deps-- 319 if parent.deps == 0 { 320 if err := s.commit(parent); err != nil { 321 return err 322 } 323 } 324 } 325 return nil 326 } 327