github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/eth/protocols/snap/sync.go (about) 1 // Copyright 2020 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 snap 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "errors" 23 "fmt" 24 gomath "math" 25 "math/big" 26 "math/rand" 27 "sort" 28 "sync" 29 "sync/atomic" 30 "time" 31 32 "github.com/tirogen/go-ethereum/common" 33 "github.com/tirogen/go-ethereum/common/math" 34 "github.com/tirogen/go-ethereum/core/rawdb" 35 "github.com/tirogen/go-ethereum/core/state" 36 "github.com/tirogen/go-ethereum/core/state/snapshot" 37 "github.com/tirogen/go-ethereum/core/types" 38 "github.com/tirogen/go-ethereum/crypto" 39 "github.com/tirogen/go-ethereum/ethdb" 40 "github.com/tirogen/go-ethereum/event" 41 "github.com/tirogen/go-ethereum/light" 42 "github.com/tirogen/go-ethereum/log" 43 "github.com/tirogen/go-ethereum/p2p/msgrate" 44 "github.com/tirogen/go-ethereum/rlp" 45 "github.com/tirogen/go-ethereum/trie" 46 "golang.org/x/crypto/sha3" 47 ) 48 49 var ( 50 // emptyRoot is the known root hash of an empty trie. 51 emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") 52 53 // emptyCode is the known hash of the empty EVM bytecode. 54 emptyCode = crypto.Keccak256Hash(nil) 55 ) 56 57 const ( 58 // minRequestSize is the minimum number of bytes to request from a remote peer. 59 // This number is used as the low cap for account and storage range requests. 60 // Bytecode and trienode are limited inherently by item count (1). 61 minRequestSize = 64 * 1024 62 63 // maxRequestSize is the maximum number of bytes to request from a remote peer. 64 // This number is used as the high cap for account and storage range requests. 65 // Bytecode and trienode are limited more explicitly by the caps below. 66 maxRequestSize = 512 * 1024 67 68 // maxCodeRequestCount is the maximum number of bytecode blobs to request in a 69 // single query. If this number is too low, we're not filling responses fully 70 // and waste round trip times. If it's too high, we're capping responses and 71 // waste bandwidth. 72 // 73 // Deployed bytecodes are currently capped at 24KB, so the minimum request 74 // size should be maxRequestSize / 24K. Assuming that most contracts do not 75 // come close to that, requesting 4x should be a good approximation. 76 maxCodeRequestCount = maxRequestSize / (24 * 1024) * 4 77 78 // maxTrieRequestCount is the maximum number of trie node blobs to request in 79 // a single query. If this number is too low, we're not filling responses fully 80 // and waste round trip times. If it's too high, we're capping responses and 81 // waste bandwidth. 82 maxTrieRequestCount = maxRequestSize / 512 83 84 // trienodeHealRateMeasurementImpact is the impact a single measurement has on 85 // the local node's trienode processing capacity. A value closer to 0 reacts 86 // slower to sudden changes, but it is also more stable against temporary hiccups. 87 trienodeHealRateMeasurementImpact = 0.005 88 89 // minTrienodeHealThrottle is the minimum divisor for throttling trie node 90 // heal requests to avoid overloading the local node and excessively expanding 91 // the state trie breadth wise. 92 minTrienodeHealThrottle = 1 93 94 // maxTrienodeHealThrottle is the maximum divisor for throttling trie node 95 // heal requests to avoid overloading the local node and exessively expanding 96 // the state trie bedth wise. 97 maxTrienodeHealThrottle = maxTrieRequestCount 98 99 // trienodeHealThrottleIncrease is the multiplier for the throttle when the 100 // rate of arriving data is higher than the rate of processing it. 101 trienodeHealThrottleIncrease = 1.33 102 103 // trienodeHealThrottleDecrease is the divisor for the throttle when the 104 // rate of arriving data is lower than the rate of processing it. 105 trienodeHealThrottleDecrease = 1.25 106 ) 107 108 var ( 109 // accountConcurrency is the number of chunks to split the account trie into 110 // to allow concurrent retrievals. 111 accountConcurrency = 16 112 113 // storageConcurrency is the number of chunks to split the a large contract 114 // storage trie into to allow concurrent retrievals. 115 storageConcurrency = 16 116 ) 117 118 // ErrCancelled is returned from snap syncing if the operation was prematurely 119 // terminated. 120 var ErrCancelled = errors.New("sync cancelled") 121 122 // accountRequest tracks a pending account range request to ensure responses are 123 // to actual requests and to validate any security constraints. 124 // 125 // Concurrency note: account requests and responses are handled concurrently from 126 // the main runloop to allow Merkle proof verifications on the peer's thread and 127 // to drop on invalid response. The request struct must contain all the data to 128 // construct the response without accessing runloop internals (i.e. task). That 129 // is only included to allow the runloop to match a response to the task being 130 // synced without having yet another set of maps. 131 type accountRequest struct { 132 peer string // Peer to which this request is assigned 133 id uint64 // Request ID of this request 134 time time.Time // Timestamp when the request was sent 135 136 deliver chan *accountResponse // Channel to deliver successful response on 137 revert chan *accountRequest // Channel to deliver request failure on 138 cancel chan struct{} // Channel to track sync cancellation 139 timeout *time.Timer // Timer to track delivery timeout 140 stale chan struct{} // Channel to signal the request was dropped 141 142 origin common.Hash // First account requested to allow continuation checks 143 limit common.Hash // Last account requested to allow non-overlapping chunking 144 145 task *accountTask // Task which this request is filling (only access fields through the runloop!!) 146 } 147 148 // accountResponse is an already Merkle-verified remote response to an account 149 // range request. It contains the subtrie for the requested account range and 150 // the database that's going to be filled with the internal nodes on commit. 151 type accountResponse struct { 152 task *accountTask // Task which this request is filling 153 154 hashes []common.Hash // Account hashes in the returned range 155 accounts []*types.StateAccount // Expanded accounts in the returned range 156 157 cont bool // Whether the account range has a continuation 158 } 159 160 // bytecodeRequest tracks a pending bytecode request to ensure responses are to 161 // actual requests and to validate any security constraints. 162 // 163 // Concurrency note: bytecode requests and responses are handled concurrently from 164 // the main runloop to allow Keccak256 hash verifications on the peer's thread and 165 // to drop on invalid response. The request struct must contain all the data to 166 // construct the response without accessing runloop internals (i.e. task). That 167 // is only included to allow the runloop to match a response to the task being 168 // synced without having yet another set of maps. 169 type bytecodeRequest struct { 170 peer string // Peer to which this request is assigned 171 id uint64 // Request ID of this request 172 time time.Time // Timestamp when the request was sent 173 174 deliver chan *bytecodeResponse // Channel to deliver successful response on 175 revert chan *bytecodeRequest // Channel to deliver request failure on 176 cancel chan struct{} // Channel to track sync cancellation 177 timeout *time.Timer // Timer to track delivery timeout 178 stale chan struct{} // Channel to signal the request was dropped 179 180 hashes []common.Hash // Bytecode hashes to validate responses 181 task *accountTask // Task which this request is filling (only access fields through the runloop!!) 182 } 183 184 // bytecodeResponse is an already verified remote response to a bytecode request. 185 type bytecodeResponse struct { 186 task *accountTask // Task which this request is filling 187 188 hashes []common.Hash // Hashes of the bytecode to avoid double hashing 189 codes [][]byte // Actual bytecodes to store into the database (nil = missing) 190 } 191 192 // storageRequest tracks a pending storage ranges request to ensure responses are 193 // to actual requests and to validate any security constraints. 194 // 195 // Concurrency note: storage requests and responses are handled concurrently from 196 // the main runloop to allow Merkle proof verifications on the peer's thread and 197 // to drop on invalid response. The request struct must contain all the data to 198 // construct the response without accessing runloop internals (i.e. tasks). That 199 // is only included to allow the runloop to match a response to the task being 200 // synced without having yet another set of maps. 201 type storageRequest struct { 202 peer string // Peer to which this request is assigned 203 id uint64 // Request ID of this request 204 time time.Time // Timestamp when the request was sent 205 206 deliver chan *storageResponse // Channel to deliver successful response on 207 revert chan *storageRequest // Channel to deliver request failure on 208 cancel chan struct{} // Channel to track sync cancellation 209 timeout *time.Timer // Timer to track delivery timeout 210 stale chan struct{} // Channel to signal the request was dropped 211 212 accounts []common.Hash // Account hashes to validate responses 213 roots []common.Hash // Storage roots to validate responses 214 215 origin common.Hash // First storage slot requested to allow continuation checks 216 limit common.Hash // Last storage slot requested to allow non-overlapping chunking 217 218 mainTask *accountTask // Task which this response belongs to (only access fields through the runloop!!) 219 subTask *storageTask // Task which this response is filling (only access fields through the runloop!!) 220 } 221 222 // storageResponse is an already Merkle-verified remote response to a storage 223 // range request. It contains the subtries for the requested storage ranges and 224 // the databases that's going to be filled with the internal nodes on commit. 225 type storageResponse struct { 226 mainTask *accountTask // Task which this response belongs to 227 subTask *storageTask // Task which this response is filling 228 229 accounts []common.Hash // Account hashes requested, may be only partially filled 230 roots []common.Hash // Storage roots requested, may be only partially filled 231 232 hashes [][]common.Hash // Storage slot hashes in the returned range 233 slots [][][]byte // Storage slot values in the returned range 234 235 cont bool // Whether the last storage range has a continuation 236 } 237 238 // trienodeHealRequest tracks a pending state trie request to ensure responses 239 // are to actual requests and to validate any security constraints. 240 // 241 // Concurrency note: trie node requests and responses are handled concurrently from 242 // the main runloop to allow Keccak256 hash verifications on the peer's thread and 243 // to drop on invalid response. The request struct must contain all the data to 244 // construct the response without accessing runloop internals (i.e. task). That 245 // is only included to allow the runloop to match a response to the task being 246 // synced without having yet another set of maps. 247 type trienodeHealRequest struct { 248 peer string // Peer to which this request is assigned 249 id uint64 // Request ID of this request 250 time time.Time // Timestamp when the request was sent 251 252 deliver chan *trienodeHealResponse // Channel to deliver successful response on 253 revert chan *trienodeHealRequest // Channel to deliver request failure on 254 cancel chan struct{} // Channel to track sync cancellation 255 timeout *time.Timer // Timer to track delivery timeout 256 stale chan struct{} // Channel to signal the request was dropped 257 258 paths []string // Trie node paths for identifying trie node 259 hashes []common.Hash // Trie node hashes to validate responses 260 261 task *healTask // Task which this request is filling (only access fields through the runloop!!) 262 } 263 264 // trienodeHealResponse is an already verified remote response to a trie node request. 265 type trienodeHealResponse struct { 266 task *healTask // Task which this request is filling 267 268 paths []string // Paths of the trie nodes 269 hashes []common.Hash // Hashes of the trie nodes to avoid double hashing 270 nodes [][]byte // Actual trie nodes to store into the database (nil = missing) 271 } 272 273 // bytecodeHealRequest tracks a pending bytecode request to ensure responses are to 274 // actual requests and to validate any security constraints. 275 // 276 // Concurrency note: bytecode requests and responses are handled concurrently from 277 // the main runloop to allow Keccak256 hash verifications on the peer's thread and 278 // to drop on invalid response. The request struct must contain all the data to 279 // construct the response without accessing runloop internals (i.e. task). That 280 // is only included to allow the runloop to match a response to the task being 281 // synced without having yet another set of maps. 282 type bytecodeHealRequest struct { 283 peer string // Peer to which this request is assigned 284 id uint64 // Request ID of this request 285 time time.Time // Timestamp when the request was sent 286 287 deliver chan *bytecodeHealResponse // Channel to deliver successful response on 288 revert chan *bytecodeHealRequest // Channel to deliver request failure on 289 cancel chan struct{} // Channel to track sync cancellation 290 timeout *time.Timer // Timer to track delivery timeout 291 stale chan struct{} // Channel to signal the request was dropped 292 293 hashes []common.Hash // Bytecode hashes to validate responses 294 task *healTask // Task which this request is filling (only access fields through the runloop!!) 295 } 296 297 // bytecodeHealResponse is an already verified remote response to a bytecode request. 298 type bytecodeHealResponse struct { 299 task *healTask // Task which this request is filling 300 301 hashes []common.Hash // Hashes of the bytecode to avoid double hashing 302 codes [][]byte // Actual bytecodes to store into the database (nil = missing) 303 } 304 305 // accountTask represents the sync task for a chunk of the account snapshot. 306 type accountTask struct { 307 // These fields get serialized to leveldb on shutdown 308 Next common.Hash // Next account to sync in this interval 309 Last common.Hash // Last account to sync in this interval 310 SubTasks map[common.Hash][]*storageTask // Storage intervals needing fetching for large contracts 311 312 // These fields are internals used during runtime 313 req *accountRequest // Pending request to fill this task 314 res *accountResponse // Validate response filling this task 315 pend int // Number of pending subtasks for this round 316 317 needCode []bool // Flags whether the filling accounts need code retrieval 318 needState []bool // Flags whether the filling accounts need storage retrieval 319 needHeal []bool // Flags whether the filling accounts's state was chunked and need healing 320 321 codeTasks map[common.Hash]struct{} // Code hashes that need retrieval 322 stateTasks map[common.Hash]common.Hash // Account hashes->roots that need full state retrieval 323 324 genBatch ethdb.Batch // Batch used by the node generator 325 genTrie *trie.StackTrie // Node generator from storage slots 326 327 done bool // Flag whether the task can be removed 328 } 329 330 // storageTask represents the sync task for a chunk of the storage snapshot. 331 type storageTask struct { 332 Next common.Hash // Next account to sync in this interval 333 Last common.Hash // Last account to sync in this interval 334 335 // These fields are internals used during runtime 336 root common.Hash // Storage root hash for this instance 337 req *storageRequest // Pending request to fill this task 338 339 genBatch ethdb.Batch // Batch used by the node generator 340 genTrie *trie.StackTrie // Node generator from storage slots 341 342 done bool // Flag whether the task can be removed 343 } 344 345 // healTask represents the sync task for healing the snap-synced chunk boundaries. 346 type healTask struct { 347 scheduler *trie.Sync // State trie sync scheduler defining the tasks 348 349 trieTasks map[string]common.Hash // Set of trie node tasks currently queued for retrieval, indexed by node path 350 codeTasks map[common.Hash]struct{} // Set of byte code tasks currently queued for retrieval, indexed by code hash 351 } 352 353 // SyncProgress is a database entry to allow suspending and resuming a snapshot state 354 // sync. Opposed to full and fast sync, there is no way to restart a suspended 355 // snap sync without prior knowledge of the suspension point. 356 type SyncProgress struct { 357 Tasks []*accountTask // The suspended account tasks (contract tasks within) 358 359 // Status report during syncing phase 360 AccountSynced uint64 // Number of accounts downloaded 361 AccountBytes common.StorageSize // Number of account trie bytes persisted to disk 362 BytecodeSynced uint64 // Number of bytecodes downloaded 363 BytecodeBytes common.StorageSize // Number of bytecode bytes downloaded 364 StorageSynced uint64 // Number of storage slots downloaded 365 StorageBytes common.StorageSize // Number of storage trie bytes persisted to disk 366 367 // Status report during healing phase 368 TrienodeHealSynced uint64 // Number of state trie nodes downloaded 369 TrienodeHealBytes common.StorageSize // Number of state trie bytes persisted to disk 370 BytecodeHealSynced uint64 // Number of bytecodes downloaded 371 BytecodeHealBytes common.StorageSize // Number of bytecodes persisted to disk 372 } 373 374 // SyncPending is analogous to SyncProgress, but it's used to report on pending 375 // ephemeral sync progress that doesn't get persisted into the database. 376 type SyncPending struct { 377 TrienodeHeal uint64 // Number of state trie nodes pending 378 BytecodeHeal uint64 // Number of bytecodes pending 379 } 380 381 // SyncPeer abstracts out the methods required for a peer to be synced against 382 // with the goal of allowing the construction of mock peers without the full 383 // blown networking. 384 type SyncPeer interface { 385 // ID retrieves the peer's unique identifier. 386 ID() string 387 388 // RequestAccountRange fetches a batch of accounts rooted in a specific account 389 // trie, starting with the origin. 390 RequestAccountRange(id uint64, root, origin, limit common.Hash, bytes uint64) error 391 392 // RequestStorageRanges fetches a batch of storage slots belonging to one or 393 // more accounts. If slots from only one account is requested, an origin marker 394 // may also be used to retrieve from there. 395 RequestStorageRanges(id uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, bytes uint64) error 396 397 // RequestByteCodes fetches a batch of bytecodes by hash. 398 RequestByteCodes(id uint64, hashes []common.Hash, bytes uint64) error 399 400 // RequestTrieNodes fetches a batch of account or storage trie nodes rooted in 401 // a specific state trie. 402 RequestTrieNodes(id uint64, root common.Hash, paths []TrieNodePathSet, bytes uint64) error 403 404 // Log retrieves the peer's own contextual logger. 405 Log() log.Logger 406 } 407 408 // Syncer is an Ethereum account and storage trie syncer based on snapshots and 409 // the snap protocol. It's purpose is to download all the accounts and storage 410 // slots from remote peers and reassemble chunks of the state trie, on top of 411 // which a state sync can be run to fix any gaps / overlaps. 412 // 413 // Every network request has a variety of failure events: 414 // - The peer disconnects after task assignment, failing to send the request 415 // - The peer disconnects after sending the request, before delivering on it 416 // - The peer remains connected, but does not deliver a response in time 417 // - The peer delivers a stale response after a previous timeout 418 // - The peer delivers a refusal to serve the requested state 419 type Syncer struct { 420 db ethdb.KeyValueStore // Database to store the trie nodes into (and dedup) 421 scheme trie.NodeScheme // Node scheme used in node database 422 423 root common.Hash // Current state trie root being synced 424 tasks []*accountTask // Current account task set being synced 425 snapped bool // Flag to signal that snap phase is done 426 healer *healTask // Current state healing task being executed 427 update chan struct{} // Notification channel for possible sync progression 428 429 peers map[string]SyncPeer // Currently active peers to download from 430 peerJoin *event.Feed // Event feed to react to peers joining 431 peerDrop *event.Feed // Event feed to react to peers dropping 432 rates *msgrate.Trackers // Message throughput rates for peers 433 434 // Request tracking during syncing phase 435 statelessPeers map[string]struct{} // Peers that failed to deliver state data 436 accountIdlers map[string]struct{} // Peers that aren't serving account requests 437 bytecodeIdlers map[string]struct{} // Peers that aren't serving bytecode requests 438 storageIdlers map[string]struct{} // Peers that aren't serving storage requests 439 440 accountReqs map[uint64]*accountRequest // Account requests currently running 441 bytecodeReqs map[uint64]*bytecodeRequest // Bytecode requests currently running 442 storageReqs map[uint64]*storageRequest // Storage requests currently running 443 444 accountSynced uint64 // Number of accounts downloaded 445 accountBytes common.StorageSize // Number of account trie bytes persisted to disk 446 bytecodeSynced uint64 // Number of bytecodes downloaded 447 bytecodeBytes common.StorageSize // Number of bytecode bytes downloaded 448 storageSynced uint64 // Number of storage slots downloaded 449 storageBytes common.StorageSize // Number of storage trie bytes persisted to disk 450 451 extProgress *SyncProgress // progress that can be exposed to external caller. 452 453 // Request tracking during healing phase 454 trienodeHealIdlers map[string]struct{} // Peers that aren't serving trie node requests 455 bytecodeHealIdlers map[string]struct{} // Peers that aren't serving bytecode requests 456 457 trienodeHealReqs map[uint64]*trienodeHealRequest // Trie node requests currently running 458 bytecodeHealReqs map[uint64]*bytecodeHealRequest // Bytecode requests currently running 459 460 trienodeHealRate float64 // Average heal rate for processing trie node data 461 trienodeHealPend uint64 // Number of trie nodes currently pending for processing 462 trienodeHealThrottle float64 // Divisor for throttling the amount of trienode heal data requested 463 trienodeHealThrottled time.Time // Timestamp the last time the throttle was updated 464 465 trienodeHealSynced uint64 // Number of state trie nodes downloaded 466 trienodeHealBytes common.StorageSize // Number of state trie bytes persisted to disk 467 trienodeHealDups uint64 // Number of state trie nodes already processed 468 trienodeHealNops uint64 // Number of state trie nodes not requested 469 bytecodeHealSynced uint64 // Number of bytecodes downloaded 470 bytecodeHealBytes common.StorageSize // Number of bytecodes persisted to disk 471 bytecodeHealDups uint64 // Number of bytecodes already processed 472 bytecodeHealNops uint64 // Number of bytecodes not requested 473 474 stateWriter ethdb.Batch // Shared batch writer used for persisting raw states 475 accountHealed uint64 // Number of accounts downloaded during the healing stage 476 accountHealedBytes common.StorageSize // Number of raw account bytes persisted to disk during the healing stage 477 storageHealed uint64 // Number of storage slots downloaded during the healing stage 478 storageHealedBytes common.StorageSize // Number of raw storage bytes persisted to disk during the healing stage 479 480 startTime time.Time // Time instance when snapshot sync started 481 logTime time.Time // Time instance when status was last reported 482 483 pend sync.WaitGroup // Tracks network request goroutines for graceful shutdown 484 lock sync.RWMutex // Protects fields that can change outside of sync (peers, reqs, root) 485 } 486 487 // NewSyncer creates a new snapshot syncer to download the Ethereum state over the 488 // snap protocol. 489 func NewSyncer(db ethdb.KeyValueStore, scheme trie.NodeScheme) *Syncer { 490 return &Syncer{ 491 db: db, 492 scheme: scheme, 493 494 peers: make(map[string]SyncPeer), 495 peerJoin: new(event.Feed), 496 peerDrop: new(event.Feed), 497 rates: msgrate.NewTrackers(log.New("proto", "snap")), 498 update: make(chan struct{}, 1), 499 500 accountIdlers: make(map[string]struct{}), 501 storageIdlers: make(map[string]struct{}), 502 bytecodeIdlers: make(map[string]struct{}), 503 504 accountReqs: make(map[uint64]*accountRequest), 505 storageReqs: make(map[uint64]*storageRequest), 506 bytecodeReqs: make(map[uint64]*bytecodeRequest), 507 508 trienodeHealIdlers: make(map[string]struct{}), 509 bytecodeHealIdlers: make(map[string]struct{}), 510 511 trienodeHealReqs: make(map[uint64]*trienodeHealRequest), 512 bytecodeHealReqs: make(map[uint64]*bytecodeHealRequest), 513 trienodeHealThrottle: maxTrienodeHealThrottle, // Tune downward instead of insta-filling with junk 514 stateWriter: db.NewBatch(), 515 516 extProgress: new(SyncProgress), 517 } 518 } 519 520 // Register injects a new data source into the syncer's peerset. 521 func (s *Syncer) Register(peer SyncPeer) error { 522 // Make sure the peer is not registered yet 523 id := peer.ID() 524 525 s.lock.Lock() 526 if _, ok := s.peers[id]; ok { 527 log.Error("Snap peer already registered", "id", id) 528 529 s.lock.Unlock() 530 return errors.New("already registered") 531 } 532 s.peers[id] = peer 533 s.rates.Track(id, msgrate.NewTracker(s.rates.MeanCapacities(), s.rates.MedianRoundTrip())) 534 535 // Mark the peer as idle, even if no sync is running 536 s.accountIdlers[id] = struct{}{} 537 s.storageIdlers[id] = struct{}{} 538 s.bytecodeIdlers[id] = struct{}{} 539 s.trienodeHealIdlers[id] = struct{}{} 540 s.bytecodeHealIdlers[id] = struct{}{} 541 s.lock.Unlock() 542 543 // Notify any active syncs that a new peer can be assigned data 544 s.peerJoin.Send(id) 545 return nil 546 } 547 548 // Unregister injects a new data source into the syncer's peerset. 549 func (s *Syncer) Unregister(id string) error { 550 // Remove all traces of the peer from the registry 551 s.lock.Lock() 552 if _, ok := s.peers[id]; !ok { 553 log.Error("Snap peer not registered", "id", id) 554 555 s.lock.Unlock() 556 return errors.New("not registered") 557 } 558 delete(s.peers, id) 559 s.rates.Untrack(id) 560 561 // Remove status markers, even if no sync is running 562 delete(s.statelessPeers, id) 563 564 delete(s.accountIdlers, id) 565 delete(s.storageIdlers, id) 566 delete(s.bytecodeIdlers, id) 567 delete(s.trienodeHealIdlers, id) 568 delete(s.bytecodeHealIdlers, id) 569 s.lock.Unlock() 570 571 // Notify any active syncs that pending requests need to be reverted 572 s.peerDrop.Send(id) 573 return nil 574 } 575 576 // Sync starts (or resumes a previous) sync cycle to iterate over a state trie 577 // with the given root and reconstruct the nodes based on the snapshot leaves. 578 // Previously downloaded segments will not be redownloaded of fixed, rather any 579 // errors will be healed after the leaves are fully accumulated. 580 func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error { 581 // Move the trie root from any previous value, revert stateless markers for 582 // any peers and initialize the syncer if it was not yet run 583 s.lock.Lock() 584 s.root = root 585 s.healer = &healTask{ 586 scheduler: state.NewStateSync(root, s.db, s.onHealState, s.scheme), 587 trieTasks: make(map[string]common.Hash), 588 codeTasks: make(map[common.Hash]struct{}), 589 } 590 s.statelessPeers = make(map[string]struct{}) 591 s.lock.Unlock() 592 593 if s.startTime == (time.Time{}) { 594 s.startTime = time.Now() 595 } 596 // Retrieve the previous sync status from LevelDB and abort if already synced 597 s.loadSyncStatus() 598 if len(s.tasks) == 0 && s.healer.scheduler.Pending() == 0 { 599 log.Debug("Snapshot sync already completed") 600 return nil 601 } 602 defer func() { // Persist any progress, independent of failure 603 for _, task := range s.tasks { 604 s.forwardAccountTask(task) 605 } 606 s.cleanAccountTasks() 607 s.saveSyncStatus() 608 }() 609 610 log.Debug("Starting snapshot sync cycle", "root", root) 611 612 // Flush out the last committed raw states 613 defer func() { 614 if s.stateWriter.ValueSize() > 0 { 615 s.stateWriter.Write() 616 s.stateWriter.Reset() 617 } 618 }() 619 defer s.report(true) 620 // commit any trie- and bytecode-healing data. 621 defer s.commitHealer(true) 622 623 // Whether sync completed or not, disregard any future packets 624 defer func() { 625 log.Debug("Terminating snapshot sync cycle", "root", root) 626 s.lock.Lock() 627 s.accountReqs = make(map[uint64]*accountRequest) 628 s.storageReqs = make(map[uint64]*storageRequest) 629 s.bytecodeReqs = make(map[uint64]*bytecodeRequest) 630 s.trienodeHealReqs = make(map[uint64]*trienodeHealRequest) 631 s.bytecodeHealReqs = make(map[uint64]*bytecodeHealRequest) 632 s.lock.Unlock() 633 }() 634 // Keep scheduling sync tasks 635 peerJoin := make(chan string, 16) 636 peerJoinSub := s.peerJoin.Subscribe(peerJoin) 637 defer peerJoinSub.Unsubscribe() 638 639 peerDrop := make(chan string, 16) 640 peerDropSub := s.peerDrop.Subscribe(peerDrop) 641 defer peerDropSub.Unsubscribe() 642 643 // Create a set of unique channels for this sync cycle. We need these to be 644 // ephemeral so a data race doesn't accidentally deliver something stale on 645 // a persistent channel across syncs (yup, this happened) 646 var ( 647 accountReqFails = make(chan *accountRequest) 648 storageReqFails = make(chan *storageRequest) 649 bytecodeReqFails = make(chan *bytecodeRequest) 650 accountResps = make(chan *accountResponse) 651 storageResps = make(chan *storageResponse) 652 bytecodeResps = make(chan *bytecodeResponse) 653 trienodeHealReqFails = make(chan *trienodeHealRequest) 654 bytecodeHealReqFails = make(chan *bytecodeHealRequest) 655 trienodeHealResps = make(chan *trienodeHealResponse) 656 bytecodeHealResps = make(chan *bytecodeHealResponse) 657 ) 658 for { 659 // Remove all completed tasks and terminate sync if everything's done 660 s.cleanStorageTasks() 661 s.cleanAccountTasks() 662 if len(s.tasks) == 0 && s.healer.scheduler.Pending() == 0 { 663 return nil 664 } 665 // Assign all the data retrieval tasks to any free peers 666 s.assignAccountTasks(accountResps, accountReqFails, cancel) 667 s.assignBytecodeTasks(bytecodeResps, bytecodeReqFails, cancel) 668 s.assignStorageTasks(storageResps, storageReqFails, cancel) 669 670 if len(s.tasks) == 0 { 671 // Sync phase done, run heal phase 672 s.assignTrienodeHealTasks(trienodeHealResps, trienodeHealReqFails, cancel) 673 s.assignBytecodeHealTasks(bytecodeHealResps, bytecodeHealReqFails, cancel) 674 } 675 // Update sync progress 676 s.lock.Lock() 677 s.extProgress = &SyncProgress{ 678 AccountSynced: s.accountSynced, 679 AccountBytes: s.accountBytes, 680 BytecodeSynced: s.bytecodeSynced, 681 BytecodeBytes: s.bytecodeBytes, 682 StorageSynced: s.storageSynced, 683 StorageBytes: s.storageBytes, 684 TrienodeHealSynced: s.trienodeHealSynced, 685 TrienodeHealBytes: s.trienodeHealBytes, 686 BytecodeHealSynced: s.bytecodeHealSynced, 687 BytecodeHealBytes: s.bytecodeHealBytes, 688 } 689 s.lock.Unlock() 690 // Wait for something to happen 691 select { 692 case <-s.update: 693 // Something happened (new peer, delivery, timeout), recheck tasks 694 case <-peerJoin: 695 // A new peer joined, try to schedule it new tasks 696 case id := <-peerDrop: 697 s.revertRequests(id) 698 case <-cancel: 699 return ErrCancelled 700 701 case req := <-accountReqFails: 702 s.revertAccountRequest(req) 703 case req := <-bytecodeReqFails: 704 s.revertBytecodeRequest(req) 705 case req := <-storageReqFails: 706 s.revertStorageRequest(req) 707 case req := <-trienodeHealReqFails: 708 s.revertTrienodeHealRequest(req) 709 case req := <-bytecodeHealReqFails: 710 s.revertBytecodeHealRequest(req) 711 712 case res := <-accountResps: 713 s.processAccountResponse(res) 714 case res := <-bytecodeResps: 715 s.processBytecodeResponse(res) 716 case res := <-storageResps: 717 s.processStorageResponse(res) 718 case res := <-trienodeHealResps: 719 s.processTrienodeHealResponse(res) 720 case res := <-bytecodeHealResps: 721 s.processBytecodeHealResponse(res) 722 } 723 // Report stats if something meaningful happened 724 s.report(false) 725 } 726 } 727 728 // loadSyncStatus retrieves a previously aborted sync status from the database, 729 // or generates a fresh one if none is available. 730 func (s *Syncer) loadSyncStatus() { 731 var progress SyncProgress 732 733 if status := rawdb.ReadSnapshotSyncStatus(s.db); status != nil { 734 if err := json.Unmarshal(status, &progress); err != nil { 735 log.Error("Failed to decode snap sync status", "err", err) 736 } else { 737 for _, task := range progress.Tasks { 738 log.Debug("Scheduled account sync task", "from", task.Next, "last", task.Last) 739 } 740 s.tasks = progress.Tasks 741 for _, task := range s.tasks { 742 task.genBatch = ethdb.HookedBatch{ 743 Batch: s.db.NewBatch(), 744 OnPut: func(key []byte, value []byte) { 745 s.accountBytes += common.StorageSize(len(key) + len(value)) 746 }, 747 } 748 task.genTrie = trie.NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, val []byte) { 749 s.scheme.WriteTrieNode(task.genBatch, owner, path, hash, val) 750 }) 751 for accountHash, subtasks := range task.SubTasks { 752 for _, subtask := range subtasks { 753 subtask.genBatch = ethdb.HookedBatch{ 754 Batch: s.db.NewBatch(), 755 OnPut: func(key []byte, value []byte) { 756 s.storageBytes += common.StorageSize(len(key) + len(value)) 757 }, 758 } 759 subtask.genTrie = trie.NewStackTrieWithOwner(func(owner common.Hash, path []byte, hash common.Hash, val []byte) { 760 s.scheme.WriteTrieNode(subtask.genBatch, owner, path, hash, val) 761 }, accountHash) 762 } 763 } 764 } 765 s.lock.Lock() 766 defer s.lock.Unlock() 767 768 s.snapped = len(s.tasks) == 0 769 770 s.accountSynced = progress.AccountSynced 771 s.accountBytes = progress.AccountBytes 772 s.bytecodeSynced = progress.BytecodeSynced 773 s.bytecodeBytes = progress.BytecodeBytes 774 s.storageSynced = progress.StorageSynced 775 s.storageBytes = progress.StorageBytes 776 777 s.trienodeHealSynced = progress.TrienodeHealSynced 778 s.trienodeHealBytes = progress.TrienodeHealBytes 779 s.bytecodeHealSynced = progress.BytecodeHealSynced 780 s.bytecodeHealBytes = progress.BytecodeHealBytes 781 return 782 } 783 } 784 // Either we've failed to decode the previous state, or there was none. 785 // Start a fresh sync by chunking up the account range and scheduling 786 // them for retrieval. 787 s.tasks = nil 788 s.accountSynced, s.accountBytes = 0, 0 789 s.bytecodeSynced, s.bytecodeBytes = 0, 0 790 s.storageSynced, s.storageBytes = 0, 0 791 s.trienodeHealSynced, s.trienodeHealBytes = 0, 0 792 s.bytecodeHealSynced, s.bytecodeHealBytes = 0, 0 793 794 var next common.Hash 795 step := new(big.Int).Sub( 796 new(big.Int).Div( 797 new(big.Int).Exp(common.Big2, common.Big256, nil), 798 big.NewInt(int64(accountConcurrency)), 799 ), common.Big1, 800 ) 801 for i := 0; i < accountConcurrency; i++ { 802 last := common.BigToHash(new(big.Int).Add(next.Big(), step)) 803 if i == accountConcurrency-1 { 804 // Make sure we don't overflow if the step is not a proper divisor 805 last = common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") 806 } 807 batch := ethdb.HookedBatch{ 808 Batch: s.db.NewBatch(), 809 OnPut: func(key []byte, value []byte) { 810 s.accountBytes += common.StorageSize(len(key) + len(value)) 811 }, 812 } 813 s.tasks = append(s.tasks, &accountTask{ 814 Next: next, 815 Last: last, 816 SubTasks: make(map[common.Hash][]*storageTask), 817 genBatch: batch, 818 genTrie: trie.NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, val []byte) { 819 s.scheme.WriteTrieNode(batch, owner, path, hash, val) 820 }), 821 }) 822 log.Debug("Created account sync task", "from", next, "last", last) 823 next = common.BigToHash(new(big.Int).Add(last.Big(), common.Big1)) 824 } 825 } 826 827 // saveSyncStatus marshals the remaining sync tasks into leveldb. 828 func (s *Syncer) saveSyncStatus() { 829 // Serialize any partial progress to disk before spinning down 830 for _, task := range s.tasks { 831 if err := task.genBatch.Write(); err != nil { 832 log.Error("Failed to persist account slots", "err", err) 833 } 834 for _, subtasks := range task.SubTasks { 835 for _, subtask := range subtasks { 836 if err := subtask.genBatch.Write(); err != nil { 837 log.Error("Failed to persist storage slots", "err", err) 838 } 839 } 840 } 841 } 842 // Store the actual progress markers 843 progress := &SyncProgress{ 844 Tasks: s.tasks, 845 AccountSynced: s.accountSynced, 846 AccountBytes: s.accountBytes, 847 BytecodeSynced: s.bytecodeSynced, 848 BytecodeBytes: s.bytecodeBytes, 849 StorageSynced: s.storageSynced, 850 StorageBytes: s.storageBytes, 851 TrienodeHealSynced: s.trienodeHealSynced, 852 TrienodeHealBytes: s.trienodeHealBytes, 853 BytecodeHealSynced: s.bytecodeHealSynced, 854 BytecodeHealBytes: s.bytecodeHealBytes, 855 } 856 status, err := json.Marshal(progress) 857 if err != nil { 858 panic(err) // This can only fail during implementation 859 } 860 rawdb.WriteSnapshotSyncStatus(s.db, status) 861 } 862 863 // Progress returns the snap sync status statistics. 864 func (s *Syncer) Progress() (*SyncProgress, *SyncPending) { 865 s.lock.Lock() 866 defer s.lock.Unlock() 867 pending := new(SyncPending) 868 if s.healer != nil { 869 pending.TrienodeHeal = uint64(len(s.healer.trieTasks)) 870 pending.BytecodeHeal = uint64(len(s.healer.codeTasks)) 871 } 872 return s.extProgress, pending 873 } 874 875 // cleanAccountTasks removes account range retrieval tasks that have already been 876 // completed. 877 func (s *Syncer) cleanAccountTasks() { 878 // If the sync was already done before, don't even bother 879 if len(s.tasks) == 0 { 880 return 881 } 882 // Sync wasn't finished previously, check for any task that can be finalized 883 for i := 0; i < len(s.tasks); i++ { 884 if s.tasks[i].done { 885 s.tasks = append(s.tasks[:i], s.tasks[i+1:]...) 886 i-- 887 } 888 } 889 // If everything was just finalized just, generate the account trie and start heal 890 if len(s.tasks) == 0 { 891 s.lock.Lock() 892 s.snapped = true 893 s.lock.Unlock() 894 895 // Push the final sync report 896 s.reportSyncProgress(true) 897 } 898 } 899 900 // cleanStorageTasks iterates over all the account tasks and storage sub-tasks 901 // within, cleaning any that have been completed. 902 func (s *Syncer) cleanStorageTasks() { 903 for _, task := range s.tasks { 904 for account, subtasks := range task.SubTasks { 905 // Remove storage range retrieval tasks that completed 906 for j := 0; j < len(subtasks); j++ { 907 if subtasks[j].done { 908 subtasks = append(subtasks[:j], subtasks[j+1:]...) 909 j-- 910 } 911 } 912 if len(subtasks) > 0 { 913 task.SubTasks[account] = subtasks 914 continue 915 } 916 // If all storage chunks are done, mark the account as done too 917 for j, hash := range task.res.hashes { 918 if hash == account { 919 task.needState[j] = false 920 } 921 } 922 delete(task.SubTasks, account) 923 task.pend-- 924 925 // If this was the last pending task, forward the account task 926 if task.pend == 0 { 927 s.forwardAccountTask(task) 928 } 929 } 930 } 931 } 932 933 // assignAccountTasks attempts to match idle peers to pending account range 934 // retrievals. 935 func (s *Syncer) assignAccountTasks(success chan *accountResponse, fail chan *accountRequest, cancel chan struct{}) { 936 s.lock.Lock() 937 defer s.lock.Unlock() 938 939 // Sort the peers by download capacity to use faster ones if many available 940 idlers := &capacitySort{ 941 ids: make([]string, 0, len(s.accountIdlers)), 942 caps: make([]int, 0, len(s.accountIdlers)), 943 } 944 targetTTL := s.rates.TargetTimeout() 945 for id := range s.accountIdlers { 946 if _, ok := s.statelessPeers[id]; ok { 947 continue 948 } 949 idlers.ids = append(idlers.ids, id) 950 idlers.caps = append(idlers.caps, s.rates.Capacity(id, AccountRangeMsg, targetTTL)) 951 } 952 if len(idlers.ids) == 0 { 953 return 954 } 955 sort.Sort(sort.Reverse(idlers)) 956 957 // Iterate over all the tasks and try to find a pending one 958 for _, task := range s.tasks { 959 // Skip any tasks already filling 960 if task.req != nil || task.res != nil { 961 continue 962 } 963 // Task pending retrieval, try to find an idle peer. If no such peer 964 // exists, we probably assigned tasks for all (or they are stateless). 965 // Abort the entire assignment mechanism. 966 if len(idlers.ids) == 0 { 967 return 968 } 969 var ( 970 idle = idlers.ids[0] 971 peer = s.peers[idle] 972 cap = idlers.caps[0] 973 ) 974 idlers.ids, idlers.caps = idlers.ids[1:], idlers.caps[1:] 975 976 // Matched a pending task to an idle peer, allocate a unique request id 977 var reqid uint64 978 for { 979 reqid = uint64(rand.Int63()) 980 if reqid == 0 { 981 continue 982 } 983 if _, ok := s.accountReqs[reqid]; ok { 984 continue 985 } 986 break 987 } 988 // Generate the network query and send it to the peer 989 req := &accountRequest{ 990 peer: idle, 991 id: reqid, 992 time: time.Now(), 993 deliver: success, 994 revert: fail, 995 cancel: cancel, 996 stale: make(chan struct{}), 997 origin: task.Next, 998 limit: task.Last, 999 task: task, 1000 } 1001 req.timeout = time.AfterFunc(s.rates.TargetTimeout(), func() { 1002 peer.Log().Debug("Account range request timed out", "reqid", reqid) 1003 s.rates.Update(idle, AccountRangeMsg, 0, 0) 1004 s.scheduleRevertAccountRequest(req) 1005 }) 1006 s.accountReqs[reqid] = req 1007 delete(s.accountIdlers, idle) 1008 1009 s.pend.Add(1) 1010 go func(root common.Hash) { 1011 defer s.pend.Done() 1012 1013 // Attempt to send the remote request and revert if it fails 1014 if cap > maxRequestSize { 1015 cap = maxRequestSize 1016 } 1017 if cap < minRequestSize { // Don't bother with peers below a bare minimum performance 1018 cap = minRequestSize 1019 } 1020 if err := peer.RequestAccountRange(reqid, root, req.origin, req.limit, uint64(cap)); err != nil { 1021 peer.Log().Debug("Failed to request account range", "err", err) 1022 s.scheduleRevertAccountRequest(req) 1023 } 1024 }(s.root) 1025 1026 // Inject the request into the task to block further assignments 1027 task.req = req 1028 } 1029 } 1030 1031 // assignBytecodeTasks attempts to match idle peers to pending code retrievals. 1032 func (s *Syncer) assignBytecodeTasks(success chan *bytecodeResponse, fail chan *bytecodeRequest, cancel chan struct{}) { 1033 s.lock.Lock() 1034 defer s.lock.Unlock() 1035 1036 // Sort the peers by download capacity to use faster ones if many available 1037 idlers := &capacitySort{ 1038 ids: make([]string, 0, len(s.bytecodeIdlers)), 1039 caps: make([]int, 0, len(s.bytecodeIdlers)), 1040 } 1041 targetTTL := s.rates.TargetTimeout() 1042 for id := range s.bytecodeIdlers { 1043 if _, ok := s.statelessPeers[id]; ok { 1044 continue 1045 } 1046 idlers.ids = append(idlers.ids, id) 1047 idlers.caps = append(idlers.caps, s.rates.Capacity(id, ByteCodesMsg, targetTTL)) 1048 } 1049 if len(idlers.ids) == 0 { 1050 return 1051 } 1052 sort.Sort(sort.Reverse(idlers)) 1053 1054 // Iterate over all the tasks and try to find a pending one 1055 for _, task := range s.tasks { 1056 // Skip any tasks not in the bytecode retrieval phase 1057 if task.res == nil { 1058 continue 1059 } 1060 // Skip tasks that are already retrieving (or done with) all codes 1061 if len(task.codeTasks) == 0 { 1062 continue 1063 } 1064 // Task pending retrieval, try to find an idle peer. If no such peer 1065 // exists, we probably assigned tasks for all (or they are stateless). 1066 // Abort the entire assignment mechanism. 1067 if len(idlers.ids) == 0 { 1068 return 1069 } 1070 var ( 1071 idle = idlers.ids[0] 1072 peer = s.peers[idle] 1073 cap = idlers.caps[0] 1074 ) 1075 idlers.ids, idlers.caps = idlers.ids[1:], idlers.caps[1:] 1076 1077 // Matched a pending task to an idle peer, allocate a unique request id 1078 var reqid uint64 1079 for { 1080 reqid = uint64(rand.Int63()) 1081 if reqid == 0 { 1082 continue 1083 } 1084 if _, ok := s.bytecodeReqs[reqid]; ok { 1085 continue 1086 } 1087 break 1088 } 1089 // Generate the network query and send it to the peer 1090 if cap > maxCodeRequestCount { 1091 cap = maxCodeRequestCount 1092 } 1093 hashes := make([]common.Hash, 0, cap) 1094 for hash := range task.codeTasks { 1095 delete(task.codeTasks, hash) 1096 hashes = append(hashes, hash) 1097 if len(hashes) >= cap { 1098 break 1099 } 1100 } 1101 req := &bytecodeRequest{ 1102 peer: idle, 1103 id: reqid, 1104 time: time.Now(), 1105 deliver: success, 1106 revert: fail, 1107 cancel: cancel, 1108 stale: make(chan struct{}), 1109 hashes: hashes, 1110 task: task, 1111 } 1112 req.timeout = time.AfterFunc(s.rates.TargetTimeout(), func() { 1113 peer.Log().Debug("Bytecode request timed out", "reqid", reqid) 1114 s.rates.Update(idle, ByteCodesMsg, 0, 0) 1115 s.scheduleRevertBytecodeRequest(req) 1116 }) 1117 s.bytecodeReqs[reqid] = req 1118 delete(s.bytecodeIdlers, idle) 1119 1120 s.pend.Add(1) 1121 go func() { 1122 defer s.pend.Done() 1123 1124 // Attempt to send the remote request and revert if it fails 1125 if err := peer.RequestByteCodes(reqid, hashes, maxRequestSize); err != nil { 1126 log.Debug("Failed to request bytecodes", "err", err) 1127 s.scheduleRevertBytecodeRequest(req) 1128 } 1129 }() 1130 } 1131 } 1132 1133 // assignStorageTasks attempts to match idle peers to pending storage range 1134 // retrievals. 1135 func (s *Syncer) assignStorageTasks(success chan *storageResponse, fail chan *storageRequest, cancel chan struct{}) { 1136 s.lock.Lock() 1137 defer s.lock.Unlock() 1138 1139 // Sort the peers by download capacity to use faster ones if many available 1140 idlers := &capacitySort{ 1141 ids: make([]string, 0, len(s.storageIdlers)), 1142 caps: make([]int, 0, len(s.storageIdlers)), 1143 } 1144 targetTTL := s.rates.TargetTimeout() 1145 for id := range s.storageIdlers { 1146 if _, ok := s.statelessPeers[id]; ok { 1147 continue 1148 } 1149 idlers.ids = append(idlers.ids, id) 1150 idlers.caps = append(idlers.caps, s.rates.Capacity(id, StorageRangesMsg, targetTTL)) 1151 } 1152 if len(idlers.ids) == 0 { 1153 return 1154 } 1155 sort.Sort(sort.Reverse(idlers)) 1156 1157 // Iterate over all the tasks and try to find a pending one 1158 for _, task := range s.tasks { 1159 // Skip any tasks not in the storage retrieval phase 1160 if task.res == nil { 1161 continue 1162 } 1163 // Skip tasks that are already retrieving (or done with) all small states 1164 if len(task.SubTasks) == 0 && len(task.stateTasks) == 0 { 1165 continue 1166 } 1167 // Task pending retrieval, try to find an idle peer. If no such peer 1168 // exists, we probably assigned tasks for all (or they are stateless). 1169 // Abort the entire assignment mechanism. 1170 if len(idlers.ids) == 0 { 1171 return 1172 } 1173 var ( 1174 idle = idlers.ids[0] 1175 peer = s.peers[idle] 1176 cap = idlers.caps[0] 1177 ) 1178 idlers.ids, idlers.caps = idlers.ids[1:], idlers.caps[1:] 1179 1180 // Matched a pending task to an idle peer, allocate a unique request id 1181 var reqid uint64 1182 for { 1183 reqid = uint64(rand.Int63()) 1184 if reqid == 0 { 1185 continue 1186 } 1187 if _, ok := s.storageReqs[reqid]; ok { 1188 continue 1189 } 1190 break 1191 } 1192 // Generate the network query and send it to the peer. If there are 1193 // large contract tasks pending, complete those before diving into 1194 // even more new contracts. 1195 if cap > maxRequestSize { 1196 cap = maxRequestSize 1197 } 1198 if cap < minRequestSize { // Don't bother with peers below a bare minimum performance 1199 cap = minRequestSize 1200 } 1201 storageSets := cap / 1024 1202 1203 var ( 1204 accounts = make([]common.Hash, 0, storageSets) 1205 roots = make([]common.Hash, 0, storageSets) 1206 subtask *storageTask 1207 ) 1208 for account, subtasks := range task.SubTasks { 1209 for _, st := range subtasks { 1210 // Skip any subtasks already filling 1211 if st.req != nil { 1212 continue 1213 } 1214 // Found an incomplete storage chunk, schedule it 1215 accounts = append(accounts, account) 1216 roots = append(roots, st.root) 1217 subtask = st 1218 break // Large contract chunks are downloaded individually 1219 } 1220 if subtask != nil { 1221 break // Large contract chunks are downloaded individually 1222 } 1223 } 1224 if subtask == nil { 1225 // No large contract required retrieval, but small ones available 1226 for account, root := range task.stateTasks { 1227 delete(task.stateTasks, account) 1228 1229 accounts = append(accounts, account) 1230 roots = append(roots, root) 1231 1232 if len(accounts) >= storageSets { 1233 break 1234 } 1235 } 1236 } 1237 // If nothing was found, it means this task is actually already fully 1238 // retrieving, but large contracts are hard to detect. Skip to the next. 1239 if len(accounts) == 0 { 1240 continue 1241 } 1242 req := &storageRequest{ 1243 peer: idle, 1244 id: reqid, 1245 time: time.Now(), 1246 deliver: success, 1247 revert: fail, 1248 cancel: cancel, 1249 stale: make(chan struct{}), 1250 accounts: accounts, 1251 roots: roots, 1252 mainTask: task, 1253 subTask: subtask, 1254 } 1255 if subtask != nil { 1256 req.origin = subtask.Next 1257 req.limit = subtask.Last 1258 } 1259 req.timeout = time.AfterFunc(s.rates.TargetTimeout(), func() { 1260 peer.Log().Debug("Storage request timed out", "reqid", reqid) 1261 s.rates.Update(idle, StorageRangesMsg, 0, 0) 1262 s.scheduleRevertStorageRequest(req) 1263 }) 1264 s.storageReqs[reqid] = req 1265 delete(s.storageIdlers, idle) 1266 1267 s.pend.Add(1) 1268 go func(root common.Hash) { 1269 defer s.pend.Done() 1270 1271 // Attempt to send the remote request and revert if it fails 1272 var origin, limit []byte 1273 if subtask != nil { 1274 origin, limit = req.origin[:], req.limit[:] 1275 } 1276 if err := peer.RequestStorageRanges(reqid, root, accounts, origin, limit, uint64(cap)); err != nil { 1277 log.Debug("Failed to request storage", "err", err) 1278 s.scheduleRevertStorageRequest(req) 1279 } 1280 }(s.root) 1281 1282 // Inject the request into the subtask to block further assignments 1283 if subtask != nil { 1284 subtask.req = req 1285 } 1286 } 1287 } 1288 1289 // assignTrienodeHealTasks attempts to match idle peers to trie node requests to 1290 // heal any trie errors caused by the snap sync's chunked retrieval model. 1291 func (s *Syncer) assignTrienodeHealTasks(success chan *trienodeHealResponse, fail chan *trienodeHealRequest, cancel chan struct{}) { 1292 s.lock.Lock() 1293 defer s.lock.Unlock() 1294 1295 // Sort the peers by download capacity to use faster ones if many available 1296 idlers := &capacitySort{ 1297 ids: make([]string, 0, len(s.trienodeHealIdlers)), 1298 caps: make([]int, 0, len(s.trienodeHealIdlers)), 1299 } 1300 targetTTL := s.rates.TargetTimeout() 1301 for id := range s.trienodeHealIdlers { 1302 if _, ok := s.statelessPeers[id]; ok { 1303 continue 1304 } 1305 idlers.ids = append(idlers.ids, id) 1306 idlers.caps = append(idlers.caps, s.rates.Capacity(id, TrieNodesMsg, targetTTL)) 1307 } 1308 if len(idlers.ids) == 0 { 1309 return 1310 } 1311 sort.Sort(sort.Reverse(idlers)) 1312 1313 // Iterate over pending tasks and try to find a peer to retrieve with 1314 for len(s.healer.trieTasks) > 0 || s.healer.scheduler.Pending() > 0 { 1315 // If there are not enough trie tasks queued to fully assign, fill the 1316 // queue from the state sync scheduler. The trie synced schedules these 1317 // together with bytecodes, so we need to queue them combined. 1318 var ( 1319 have = len(s.healer.trieTasks) + len(s.healer.codeTasks) 1320 want = maxTrieRequestCount + maxCodeRequestCount 1321 ) 1322 if have < want { 1323 paths, hashes, codes := s.healer.scheduler.Missing(want - have) 1324 for i, path := range paths { 1325 s.healer.trieTasks[path] = hashes[i] 1326 } 1327 for _, hash := range codes { 1328 s.healer.codeTasks[hash] = struct{}{} 1329 } 1330 } 1331 // If all the heal tasks are bytecodes or already downloading, bail 1332 if len(s.healer.trieTasks) == 0 { 1333 return 1334 } 1335 // Task pending retrieval, try to find an idle peer. If no such peer 1336 // exists, we probably assigned tasks for all (or they are stateless). 1337 // Abort the entire assignment mechanism. 1338 if len(idlers.ids) == 0 { 1339 return 1340 } 1341 var ( 1342 idle = idlers.ids[0] 1343 peer = s.peers[idle] 1344 cap = idlers.caps[0] 1345 ) 1346 idlers.ids, idlers.caps = idlers.ids[1:], idlers.caps[1:] 1347 1348 // Matched a pending task to an idle peer, allocate a unique request id 1349 var reqid uint64 1350 for { 1351 reqid = uint64(rand.Int63()) 1352 if reqid == 0 { 1353 continue 1354 } 1355 if _, ok := s.trienodeHealReqs[reqid]; ok { 1356 continue 1357 } 1358 break 1359 } 1360 // Generate the network query and send it to the peer 1361 if cap > maxTrieRequestCount { 1362 cap = maxTrieRequestCount 1363 } 1364 cap = int(float64(cap) / s.trienodeHealThrottle) 1365 if cap <= 0 { 1366 cap = 1 1367 } 1368 var ( 1369 hashes = make([]common.Hash, 0, cap) 1370 paths = make([]string, 0, cap) 1371 pathsets = make([]TrieNodePathSet, 0, cap) 1372 ) 1373 for path, hash := range s.healer.trieTasks { 1374 delete(s.healer.trieTasks, path) 1375 1376 paths = append(paths, path) 1377 hashes = append(hashes, hash) 1378 if len(paths) >= cap { 1379 break 1380 } 1381 } 1382 // Group requests by account hash 1383 paths, hashes, _, pathsets = sortByAccountPath(paths, hashes) 1384 req := &trienodeHealRequest{ 1385 peer: idle, 1386 id: reqid, 1387 time: time.Now(), 1388 deliver: success, 1389 revert: fail, 1390 cancel: cancel, 1391 stale: make(chan struct{}), 1392 paths: paths, 1393 hashes: hashes, 1394 task: s.healer, 1395 } 1396 req.timeout = time.AfterFunc(s.rates.TargetTimeout(), func() { 1397 peer.Log().Debug("Trienode heal request timed out", "reqid", reqid) 1398 s.rates.Update(idle, TrieNodesMsg, 0, 0) 1399 s.scheduleRevertTrienodeHealRequest(req) 1400 }) 1401 s.trienodeHealReqs[reqid] = req 1402 delete(s.trienodeHealIdlers, idle) 1403 1404 s.pend.Add(1) 1405 go func(root common.Hash) { 1406 defer s.pend.Done() 1407 1408 // Attempt to send the remote request and revert if it fails 1409 if err := peer.RequestTrieNodes(reqid, root, pathsets, maxRequestSize); err != nil { 1410 log.Debug("Failed to request trienode healers", "err", err) 1411 s.scheduleRevertTrienodeHealRequest(req) 1412 } 1413 }(s.root) 1414 } 1415 } 1416 1417 // assignBytecodeHealTasks attempts to match idle peers to bytecode requests to 1418 // heal any trie errors caused by the snap sync's chunked retrieval model. 1419 func (s *Syncer) assignBytecodeHealTasks(success chan *bytecodeHealResponse, fail chan *bytecodeHealRequest, cancel chan struct{}) { 1420 s.lock.Lock() 1421 defer s.lock.Unlock() 1422 1423 // Sort the peers by download capacity to use faster ones if many available 1424 idlers := &capacitySort{ 1425 ids: make([]string, 0, len(s.bytecodeHealIdlers)), 1426 caps: make([]int, 0, len(s.bytecodeHealIdlers)), 1427 } 1428 targetTTL := s.rates.TargetTimeout() 1429 for id := range s.bytecodeHealIdlers { 1430 if _, ok := s.statelessPeers[id]; ok { 1431 continue 1432 } 1433 idlers.ids = append(idlers.ids, id) 1434 idlers.caps = append(idlers.caps, s.rates.Capacity(id, ByteCodesMsg, targetTTL)) 1435 } 1436 if len(idlers.ids) == 0 { 1437 return 1438 } 1439 sort.Sort(sort.Reverse(idlers)) 1440 1441 // Iterate over pending tasks and try to find a peer to retrieve with 1442 for len(s.healer.codeTasks) > 0 || s.healer.scheduler.Pending() > 0 { 1443 // If there are not enough trie tasks queued to fully assign, fill the 1444 // queue from the state sync scheduler. The trie synced schedules these 1445 // together with trie nodes, so we need to queue them combined. 1446 var ( 1447 have = len(s.healer.trieTasks) + len(s.healer.codeTasks) 1448 want = maxTrieRequestCount + maxCodeRequestCount 1449 ) 1450 if have < want { 1451 paths, hashes, codes := s.healer.scheduler.Missing(want - have) 1452 for i, path := range paths { 1453 s.healer.trieTasks[path] = hashes[i] 1454 } 1455 for _, hash := range codes { 1456 s.healer.codeTasks[hash] = struct{}{} 1457 } 1458 } 1459 // If all the heal tasks are trienodes or already downloading, bail 1460 if len(s.healer.codeTasks) == 0 { 1461 return 1462 } 1463 // Task pending retrieval, try to find an idle peer. If no such peer 1464 // exists, we probably assigned tasks for all (or they are stateless). 1465 // Abort the entire assignment mechanism. 1466 if len(idlers.ids) == 0 { 1467 return 1468 } 1469 var ( 1470 idle = idlers.ids[0] 1471 peer = s.peers[idle] 1472 cap = idlers.caps[0] 1473 ) 1474 idlers.ids, idlers.caps = idlers.ids[1:], idlers.caps[1:] 1475 1476 // Matched a pending task to an idle peer, allocate a unique request id 1477 var reqid uint64 1478 for { 1479 reqid = uint64(rand.Int63()) 1480 if reqid == 0 { 1481 continue 1482 } 1483 if _, ok := s.bytecodeHealReqs[reqid]; ok { 1484 continue 1485 } 1486 break 1487 } 1488 // Generate the network query and send it to the peer 1489 if cap > maxCodeRequestCount { 1490 cap = maxCodeRequestCount 1491 } 1492 hashes := make([]common.Hash, 0, cap) 1493 for hash := range s.healer.codeTasks { 1494 delete(s.healer.codeTasks, hash) 1495 1496 hashes = append(hashes, hash) 1497 if len(hashes) >= cap { 1498 break 1499 } 1500 } 1501 req := &bytecodeHealRequest{ 1502 peer: idle, 1503 id: reqid, 1504 time: time.Now(), 1505 deliver: success, 1506 revert: fail, 1507 cancel: cancel, 1508 stale: make(chan struct{}), 1509 hashes: hashes, 1510 task: s.healer, 1511 } 1512 req.timeout = time.AfterFunc(s.rates.TargetTimeout(), func() { 1513 peer.Log().Debug("Bytecode heal request timed out", "reqid", reqid) 1514 s.rates.Update(idle, ByteCodesMsg, 0, 0) 1515 s.scheduleRevertBytecodeHealRequest(req) 1516 }) 1517 s.bytecodeHealReqs[reqid] = req 1518 delete(s.bytecodeHealIdlers, idle) 1519 1520 s.pend.Add(1) 1521 go func() { 1522 defer s.pend.Done() 1523 1524 // Attempt to send the remote request and revert if it fails 1525 if err := peer.RequestByteCodes(reqid, hashes, maxRequestSize); err != nil { 1526 log.Debug("Failed to request bytecode healers", "err", err) 1527 s.scheduleRevertBytecodeHealRequest(req) 1528 } 1529 }() 1530 } 1531 } 1532 1533 // revertRequests locates all the currently pending requests from a particular 1534 // peer and reverts them, rescheduling for others to fulfill. 1535 func (s *Syncer) revertRequests(peer string) { 1536 // Gather the requests first, revertals need the lock too 1537 s.lock.Lock() 1538 var accountReqs []*accountRequest 1539 for _, req := range s.accountReqs { 1540 if req.peer == peer { 1541 accountReqs = append(accountReqs, req) 1542 } 1543 } 1544 var bytecodeReqs []*bytecodeRequest 1545 for _, req := range s.bytecodeReqs { 1546 if req.peer == peer { 1547 bytecodeReqs = append(bytecodeReqs, req) 1548 } 1549 } 1550 var storageReqs []*storageRequest 1551 for _, req := range s.storageReqs { 1552 if req.peer == peer { 1553 storageReqs = append(storageReqs, req) 1554 } 1555 } 1556 var trienodeHealReqs []*trienodeHealRequest 1557 for _, req := range s.trienodeHealReqs { 1558 if req.peer == peer { 1559 trienodeHealReqs = append(trienodeHealReqs, req) 1560 } 1561 } 1562 var bytecodeHealReqs []*bytecodeHealRequest 1563 for _, req := range s.bytecodeHealReqs { 1564 if req.peer == peer { 1565 bytecodeHealReqs = append(bytecodeHealReqs, req) 1566 } 1567 } 1568 s.lock.Unlock() 1569 1570 // Revert all the requests matching the peer 1571 for _, req := range accountReqs { 1572 s.revertAccountRequest(req) 1573 } 1574 for _, req := range bytecodeReqs { 1575 s.revertBytecodeRequest(req) 1576 } 1577 for _, req := range storageReqs { 1578 s.revertStorageRequest(req) 1579 } 1580 for _, req := range trienodeHealReqs { 1581 s.revertTrienodeHealRequest(req) 1582 } 1583 for _, req := range bytecodeHealReqs { 1584 s.revertBytecodeHealRequest(req) 1585 } 1586 } 1587 1588 // scheduleRevertAccountRequest asks the event loop to clean up an account range 1589 // request and return all failed retrieval tasks to the scheduler for reassignment. 1590 func (s *Syncer) scheduleRevertAccountRequest(req *accountRequest) { 1591 select { 1592 case req.revert <- req: 1593 // Sync event loop notified 1594 case <-req.cancel: 1595 // Sync cycle got cancelled 1596 case <-req.stale: 1597 // Request already reverted 1598 } 1599 } 1600 1601 // revertAccountRequest cleans up an account range request and returns all failed 1602 // retrieval tasks to the scheduler for reassignment. 1603 // 1604 // Note, this needs to run on the event runloop thread to reschedule to idle peers. 1605 // On peer threads, use scheduleRevertAccountRequest. 1606 func (s *Syncer) revertAccountRequest(req *accountRequest) { 1607 log.Debug("Reverting account request", "peer", req.peer, "reqid", req.id) 1608 select { 1609 case <-req.stale: 1610 log.Trace("Account request already reverted", "peer", req.peer, "reqid", req.id) 1611 return 1612 default: 1613 } 1614 close(req.stale) 1615 1616 // Remove the request from the tracked set 1617 s.lock.Lock() 1618 delete(s.accountReqs, req.id) 1619 s.lock.Unlock() 1620 1621 // If there's a timeout timer still running, abort it and mark the account 1622 // task as not-pending, ready for rescheduling 1623 req.timeout.Stop() 1624 if req.task.req == req { 1625 req.task.req = nil 1626 } 1627 } 1628 1629 // scheduleRevertBytecodeRequest asks the event loop to clean up a bytecode request 1630 // and return all failed retrieval tasks to the scheduler for reassignment. 1631 func (s *Syncer) scheduleRevertBytecodeRequest(req *bytecodeRequest) { 1632 select { 1633 case req.revert <- req: 1634 // Sync event loop notified 1635 case <-req.cancel: 1636 // Sync cycle got cancelled 1637 case <-req.stale: 1638 // Request already reverted 1639 } 1640 } 1641 1642 // revertBytecodeRequest cleans up a bytecode request and returns all failed 1643 // retrieval tasks to the scheduler for reassignment. 1644 // 1645 // Note, this needs to run on the event runloop thread to reschedule to idle peers. 1646 // On peer threads, use scheduleRevertBytecodeRequest. 1647 func (s *Syncer) revertBytecodeRequest(req *bytecodeRequest) { 1648 log.Debug("Reverting bytecode request", "peer", req.peer) 1649 select { 1650 case <-req.stale: 1651 log.Trace("Bytecode request already reverted", "peer", req.peer, "reqid", req.id) 1652 return 1653 default: 1654 } 1655 close(req.stale) 1656 1657 // Remove the request from the tracked set 1658 s.lock.Lock() 1659 delete(s.bytecodeReqs, req.id) 1660 s.lock.Unlock() 1661 1662 // If there's a timeout timer still running, abort it and mark the code 1663 // retrievals as not-pending, ready for rescheduling 1664 req.timeout.Stop() 1665 for _, hash := range req.hashes { 1666 req.task.codeTasks[hash] = struct{}{} 1667 } 1668 } 1669 1670 // scheduleRevertStorageRequest asks the event loop to clean up a storage range 1671 // request and return all failed retrieval tasks to the scheduler for reassignment. 1672 func (s *Syncer) scheduleRevertStorageRequest(req *storageRequest) { 1673 select { 1674 case req.revert <- req: 1675 // Sync event loop notified 1676 case <-req.cancel: 1677 // Sync cycle got cancelled 1678 case <-req.stale: 1679 // Request already reverted 1680 } 1681 } 1682 1683 // revertStorageRequest cleans up a storage range request and returns all failed 1684 // retrieval tasks to the scheduler for reassignment. 1685 // 1686 // Note, this needs to run on the event runloop thread to reschedule to idle peers. 1687 // On peer threads, use scheduleRevertStorageRequest. 1688 func (s *Syncer) revertStorageRequest(req *storageRequest) { 1689 log.Debug("Reverting storage request", "peer", req.peer) 1690 select { 1691 case <-req.stale: 1692 log.Trace("Storage request already reverted", "peer", req.peer, "reqid", req.id) 1693 return 1694 default: 1695 } 1696 close(req.stale) 1697 1698 // Remove the request from the tracked set 1699 s.lock.Lock() 1700 delete(s.storageReqs, req.id) 1701 s.lock.Unlock() 1702 1703 // If there's a timeout timer still running, abort it and mark the storage 1704 // task as not-pending, ready for rescheduling 1705 req.timeout.Stop() 1706 if req.subTask != nil { 1707 req.subTask.req = nil 1708 } else { 1709 for i, account := range req.accounts { 1710 req.mainTask.stateTasks[account] = req.roots[i] 1711 } 1712 } 1713 } 1714 1715 // scheduleRevertTrienodeHealRequest asks the event loop to clean up a trienode heal 1716 // request and return all failed retrieval tasks to the scheduler for reassignment. 1717 func (s *Syncer) scheduleRevertTrienodeHealRequest(req *trienodeHealRequest) { 1718 select { 1719 case req.revert <- req: 1720 // Sync event loop notified 1721 case <-req.cancel: 1722 // Sync cycle got cancelled 1723 case <-req.stale: 1724 // Request already reverted 1725 } 1726 } 1727 1728 // revertTrienodeHealRequest cleans up a trienode heal request and returns all 1729 // failed retrieval tasks to the scheduler for reassignment. 1730 // 1731 // Note, this needs to run on the event runloop thread to reschedule to idle peers. 1732 // On peer threads, use scheduleRevertTrienodeHealRequest. 1733 func (s *Syncer) revertTrienodeHealRequest(req *trienodeHealRequest) { 1734 log.Debug("Reverting trienode heal request", "peer", req.peer) 1735 select { 1736 case <-req.stale: 1737 log.Trace("Trienode heal request already reverted", "peer", req.peer, "reqid", req.id) 1738 return 1739 default: 1740 } 1741 close(req.stale) 1742 1743 // Remove the request from the tracked set 1744 s.lock.Lock() 1745 delete(s.trienodeHealReqs, req.id) 1746 s.lock.Unlock() 1747 1748 // If there's a timeout timer still running, abort it and mark the trie node 1749 // retrievals as not-pending, ready for rescheduling 1750 req.timeout.Stop() 1751 for i, path := range req.paths { 1752 req.task.trieTasks[path] = req.hashes[i] 1753 } 1754 } 1755 1756 // scheduleRevertBytecodeHealRequest asks the event loop to clean up a bytecode heal 1757 // request and return all failed retrieval tasks to the scheduler for reassignment. 1758 func (s *Syncer) scheduleRevertBytecodeHealRequest(req *bytecodeHealRequest) { 1759 select { 1760 case req.revert <- req: 1761 // Sync event loop notified 1762 case <-req.cancel: 1763 // Sync cycle got cancelled 1764 case <-req.stale: 1765 // Request already reverted 1766 } 1767 } 1768 1769 // revertBytecodeHealRequest cleans up a bytecode heal request and returns all 1770 // failed retrieval tasks to the scheduler for reassignment. 1771 // 1772 // Note, this needs to run on the event runloop thread to reschedule to idle peers. 1773 // On peer threads, use scheduleRevertBytecodeHealRequest. 1774 func (s *Syncer) revertBytecodeHealRequest(req *bytecodeHealRequest) { 1775 log.Debug("Reverting bytecode heal request", "peer", req.peer) 1776 select { 1777 case <-req.stale: 1778 log.Trace("Bytecode heal request already reverted", "peer", req.peer, "reqid", req.id) 1779 return 1780 default: 1781 } 1782 close(req.stale) 1783 1784 // Remove the request from the tracked set 1785 s.lock.Lock() 1786 delete(s.bytecodeHealReqs, req.id) 1787 s.lock.Unlock() 1788 1789 // If there's a timeout timer still running, abort it and mark the code 1790 // retrievals as not-pending, ready for rescheduling 1791 req.timeout.Stop() 1792 for _, hash := range req.hashes { 1793 req.task.codeTasks[hash] = struct{}{} 1794 } 1795 } 1796 1797 // processAccountResponse integrates an already validated account range response 1798 // into the account tasks. 1799 func (s *Syncer) processAccountResponse(res *accountResponse) { 1800 // Switch the task from pending to filling 1801 res.task.req = nil 1802 res.task.res = res 1803 1804 // Ensure that the response doesn't overflow into the subsequent task 1805 last := res.task.Last.Big() 1806 for i, hash := range res.hashes { 1807 // Mark the range complete if the last is already included. 1808 // Keep iteration to delete the extra states if exists. 1809 cmp := hash.Big().Cmp(last) 1810 if cmp == 0 { 1811 res.cont = false 1812 continue 1813 } 1814 if cmp > 0 { 1815 // Chunk overflown, cut off excess 1816 res.hashes = res.hashes[:i] 1817 res.accounts = res.accounts[:i] 1818 res.cont = false // Mark range completed 1819 break 1820 } 1821 } 1822 // Iterate over all the accounts and assemble which ones need further sub- 1823 // filling before the entire account range can be persisted. 1824 res.task.needCode = make([]bool, len(res.accounts)) 1825 res.task.needState = make([]bool, len(res.accounts)) 1826 res.task.needHeal = make([]bool, len(res.accounts)) 1827 1828 res.task.codeTasks = make(map[common.Hash]struct{}) 1829 res.task.stateTasks = make(map[common.Hash]common.Hash) 1830 1831 resumed := make(map[common.Hash]struct{}) 1832 1833 res.task.pend = 0 1834 for i, account := range res.accounts { 1835 // Check if the account is a contract with an unknown code 1836 if !bytes.Equal(account.CodeHash, emptyCode[:]) { 1837 if !rawdb.HasCodeWithPrefix(s.db, common.BytesToHash(account.CodeHash)) { 1838 res.task.codeTasks[common.BytesToHash(account.CodeHash)] = struct{}{} 1839 res.task.needCode[i] = true 1840 res.task.pend++ 1841 } 1842 } 1843 // Check if the account is a contract with an unknown storage trie 1844 if account.Root != emptyRoot { 1845 if !s.scheme.HasTrieNode(s.db, res.hashes[i], nil, account.Root) { 1846 // If there was a previous large state retrieval in progress, 1847 // don't restart it from scratch. This happens if a sync cycle 1848 // is interrupted and resumed later. However, *do* update the 1849 // previous root hash. 1850 if subtasks, ok := res.task.SubTasks[res.hashes[i]]; ok { 1851 log.Debug("Resuming large storage retrieval", "account", res.hashes[i], "root", account.Root) 1852 for _, subtask := range subtasks { 1853 subtask.root = account.Root 1854 } 1855 res.task.needHeal[i] = true 1856 resumed[res.hashes[i]] = struct{}{} 1857 } else { 1858 res.task.stateTasks[res.hashes[i]] = account.Root 1859 } 1860 res.task.needState[i] = true 1861 res.task.pend++ 1862 } 1863 } 1864 } 1865 // Delete any subtasks that have been aborted but not resumed. This may undo 1866 // some progress if a new peer gives us less accounts than an old one, but for 1867 // now we have to live with that. 1868 for hash := range res.task.SubTasks { 1869 if _, ok := resumed[hash]; !ok { 1870 log.Debug("Aborting suspended storage retrieval", "account", hash) 1871 delete(res.task.SubTasks, hash) 1872 } 1873 } 1874 // If the account range contained no contracts, or all have been fully filled 1875 // beforehand, short circuit storage filling and forward to the next task 1876 if res.task.pend == 0 { 1877 s.forwardAccountTask(res.task) 1878 return 1879 } 1880 // Some accounts are incomplete, leave as is for the storage and contract 1881 // task assigners to pick up and fill. 1882 } 1883 1884 // processBytecodeResponse integrates an already validated bytecode response 1885 // into the account tasks. 1886 func (s *Syncer) processBytecodeResponse(res *bytecodeResponse) { 1887 batch := s.db.NewBatch() 1888 1889 var ( 1890 codes uint64 1891 ) 1892 for i, hash := range res.hashes { 1893 code := res.codes[i] 1894 1895 // If the bytecode was not delivered, reschedule it 1896 if code == nil { 1897 res.task.codeTasks[hash] = struct{}{} 1898 continue 1899 } 1900 // Code was delivered, mark it not needed any more 1901 for j, account := range res.task.res.accounts { 1902 if res.task.needCode[j] && hash == common.BytesToHash(account.CodeHash) { 1903 res.task.needCode[j] = false 1904 res.task.pend-- 1905 } 1906 } 1907 // Push the bytecode into a database batch 1908 codes++ 1909 rawdb.WriteCode(batch, hash, code) 1910 } 1911 bytes := common.StorageSize(batch.ValueSize()) 1912 if err := batch.Write(); err != nil { 1913 log.Crit("Failed to persist bytecodes", "err", err) 1914 } 1915 s.bytecodeSynced += codes 1916 s.bytecodeBytes += bytes 1917 1918 log.Debug("Persisted set of bytecodes", "count", codes, "bytes", bytes) 1919 1920 // If this delivery completed the last pending task, forward the account task 1921 // to the next chunk 1922 if res.task.pend == 0 { 1923 s.forwardAccountTask(res.task) 1924 return 1925 } 1926 // Some accounts are still incomplete, leave as is for the storage and contract 1927 // task assigners to pick up and fill. 1928 } 1929 1930 // processStorageResponse integrates an already validated storage response 1931 // into the account tasks. 1932 func (s *Syncer) processStorageResponse(res *storageResponse) { 1933 // Switch the subtask from pending to idle 1934 if res.subTask != nil { 1935 res.subTask.req = nil 1936 } 1937 batch := ethdb.HookedBatch{ 1938 Batch: s.db.NewBatch(), 1939 OnPut: func(key []byte, value []byte) { 1940 s.storageBytes += common.StorageSize(len(key) + len(value)) 1941 }, 1942 } 1943 var ( 1944 slots int 1945 oldStorageBytes = s.storageBytes 1946 ) 1947 // Iterate over all the accounts and reconstruct their storage tries from the 1948 // delivered slots 1949 for i, account := range res.accounts { 1950 // If the account was not delivered, reschedule it 1951 if i >= len(res.hashes) { 1952 res.mainTask.stateTasks[account] = res.roots[i] 1953 continue 1954 } 1955 // State was delivered, if complete mark as not needed any more, otherwise 1956 // mark the account as needing healing 1957 for j, hash := range res.mainTask.res.hashes { 1958 if account != hash { 1959 continue 1960 } 1961 acc := res.mainTask.res.accounts[j] 1962 1963 // If the packet contains multiple contract storage slots, all 1964 // but the last are surely complete. The last contract may be 1965 // chunked, so check it's continuation flag. 1966 if res.subTask == nil && res.mainTask.needState[j] && (i < len(res.hashes)-1 || !res.cont) { 1967 res.mainTask.needState[j] = false 1968 res.mainTask.pend-- 1969 } 1970 // If the last contract was chunked, mark it as needing healing 1971 // to avoid writing it out to disk prematurely. 1972 if res.subTask == nil && !res.mainTask.needHeal[j] && i == len(res.hashes)-1 && res.cont { 1973 res.mainTask.needHeal[j] = true 1974 } 1975 // If the last contract was chunked, we need to switch to large 1976 // contract handling mode 1977 if res.subTask == nil && i == len(res.hashes)-1 && res.cont { 1978 // If we haven't yet started a large-contract retrieval, create 1979 // the subtasks for it within the main account task 1980 if tasks, ok := res.mainTask.SubTasks[account]; !ok { 1981 var ( 1982 keys = res.hashes[i] 1983 chunks = uint64(storageConcurrency) 1984 lastKey common.Hash 1985 ) 1986 if len(keys) > 0 { 1987 lastKey = keys[len(keys)-1] 1988 } 1989 // If the number of slots remaining is low, decrease the 1990 // number of chunks. Somewhere on the order of 10-15K slots 1991 // fit into a packet of 500KB. A key/slot pair is maximum 64 1992 // bytes, so pessimistically maxRequestSize/64 = 8K. 1993 // 1994 // Chunk so that at least 2 packets are needed to fill a task. 1995 if estimate, err := estimateRemainingSlots(len(keys), lastKey); err == nil { 1996 if n := estimate / (2 * (maxRequestSize / 64)); n+1 < chunks { 1997 chunks = n + 1 1998 } 1999 log.Debug("Chunked large contract", "initiators", len(keys), "tail", lastKey, "remaining", estimate, "chunks", chunks) 2000 } else { 2001 log.Debug("Chunked large contract", "initiators", len(keys), "tail", lastKey, "chunks", chunks) 2002 } 2003 r := newHashRange(lastKey, chunks) 2004 2005 // Our first task is the one that was just filled by this response. 2006 batch := ethdb.HookedBatch{ 2007 Batch: s.db.NewBatch(), 2008 OnPut: func(key []byte, value []byte) { 2009 s.storageBytes += common.StorageSize(len(key) + len(value)) 2010 }, 2011 } 2012 tasks = append(tasks, &storageTask{ 2013 Next: common.Hash{}, 2014 Last: r.End(), 2015 root: acc.Root, 2016 genBatch: batch, 2017 genTrie: trie.NewStackTrieWithOwner(func(owner common.Hash, path []byte, hash common.Hash, val []byte) { 2018 s.scheme.WriteTrieNode(batch, owner, path, hash, val) 2019 }, account), 2020 }) 2021 for r.Next() { 2022 batch := ethdb.HookedBatch{ 2023 Batch: s.db.NewBatch(), 2024 OnPut: func(key []byte, value []byte) { 2025 s.storageBytes += common.StorageSize(len(key) + len(value)) 2026 }, 2027 } 2028 tasks = append(tasks, &storageTask{ 2029 Next: r.Start(), 2030 Last: r.End(), 2031 root: acc.Root, 2032 genBatch: batch, 2033 genTrie: trie.NewStackTrieWithOwner(func(owner common.Hash, path []byte, hash common.Hash, val []byte) { 2034 s.scheme.WriteTrieNode(batch, owner, path, hash, val) 2035 }, account), 2036 }) 2037 } 2038 for _, task := range tasks { 2039 log.Debug("Created storage sync task", "account", account, "root", acc.Root, "from", task.Next, "last", task.Last) 2040 } 2041 res.mainTask.SubTasks[account] = tasks 2042 2043 // Since we've just created the sub-tasks, this response 2044 // is surely for the first one (zero origin) 2045 res.subTask = tasks[0] 2046 } 2047 } 2048 // If we're in large contract delivery mode, forward the subtask 2049 if res.subTask != nil { 2050 // Ensure the response doesn't overflow into the subsequent task 2051 last := res.subTask.Last.Big() 2052 // Find the first overflowing key. While at it, mark res as complete 2053 // if we find the range to include or pass the 'last' 2054 index := sort.Search(len(res.hashes[i]), func(k int) bool { 2055 cmp := res.hashes[i][k].Big().Cmp(last) 2056 if cmp >= 0 { 2057 res.cont = false 2058 } 2059 return cmp > 0 2060 }) 2061 if index >= 0 { 2062 // cut off excess 2063 res.hashes[i] = res.hashes[i][:index] 2064 res.slots[i] = res.slots[i][:index] 2065 } 2066 // Forward the relevant storage chunk (even if created just now) 2067 if res.cont { 2068 res.subTask.Next = incHash(res.hashes[i][len(res.hashes[i])-1]) 2069 } else { 2070 res.subTask.done = true 2071 } 2072 } 2073 } 2074 // Iterate over all the complete contracts, reconstruct the trie nodes and 2075 // push them to disk. If the contract is chunked, the trie nodes will be 2076 // reconstructed later. 2077 slots += len(res.hashes[i]) 2078 2079 if i < len(res.hashes)-1 || res.subTask == nil { 2080 tr := trie.NewStackTrieWithOwner(func(owner common.Hash, path []byte, hash common.Hash, val []byte) { 2081 s.scheme.WriteTrieNode(batch, owner, path, hash, val) 2082 }, account) 2083 for j := 0; j < len(res.hashes[i]); j++ { 2084 tr.Update(res.hashes[i][j][:], res.slots[i][j]) 2085 } 2086 tr.Commit() 2087 } 2088 // Persist the received storage segments. These flat state maybe 2089 // outdated during the sync, but it can be fixed later during the 2090 // snapshot generation. 2091 for j := 0; j < len(res.hashes[i]); j++ { 2092 rawdb.WriteStorageSnapshot(batch, account, res.hashes[i][j], res.slots[i][j]) 2093 2094 // If we're storing large contracts, generate the trie nodes 2095 // on the fly to not trash the gluing points 2096 if i == len(res.hashes)-1 && res.subTask != nil { 2097 res.subTask.genTrie.Update(res.hashes[i][j][:], res.slots[i][j]) 2098 } 2099 } 2100 } 2101 // Large contracts could have generated new trie nodes, flush them to disk 2102 if res.subTask != nil { 2103 if res.subTask.done { 2104 if root, err := res.subTask.genTrie.Commit(); err != nil { 2105 log.Error("Failed to commit stack slots", "err", err) 2106 } else if root == res.subTask.root { 2107 // If the chunk's root is an overflown but full delivery, clear the heal request 2108 for i, account := range res.mainTask.res.hashes { 2109 if account == res.accounts[len(res.accounts)-1] { 2110 res.mainTask.needHeal[i] = false 2111 } 2112 } 2113 } 2114 } 2115 if res.subTask.genBatch.ValueSize() > ethdb.IdealBatchSize || res.subTask.done { 2116 if err := res.subTask.genBatch.Write(); err != nil { 2117 log.Error("Failed to persist stack slots", "err", err) 2118 } 2119 res.subTask.genBatch.Reset() 2120 } 2121 } 2122 // Flush anything written just now and update the stats 2123 if err := batch.Write(); err != nil { 2124 log.Crit("Failed to persist storage slots", "err", err) 2125 } 2126 s.storageSynced += uint64(slots) 2127 2128 log.Debug("Persisted set of storage slots", "accounts", len(res.hashes), "slots", slots, "bytes", s.storageBytes-oldStorageBytes) 2129 2130 // If this delivery completed the last pending task, forward the account task 2131 // to the next chunk 2132 if res.mainTask.pend == 0 { 2133 s.forwardAccountTask(res.mainTask) 2134 return 2135 } 2136 // Some accounts are still incomplete, leave as is for the storage and contract 2137 // task assigners to pick up and fill. 2138 } 2139 2140 // processTrienodeHealResponse integrates an already validated trienode response 2141 // into the healer tasks. 2142 func (s *Syncer) processTrienodeHealResponse(res *trienodeHealResponse) { 2143 var ( 2144 start = time.Now() 2145 fills int 2146 ) 2147 for i, hash := range res.hashes { 2148 node := res.nodes[i] 2149 2150 // If the trie node was not delivered, reschedule it 2151 if node == nil { 2152 res.task.trieTasks[res.paths[i]] = res.hashes[i] 2153 continue 2154 } 2155 fills++ 2156 2157 // Push the trie node into the state syncer 2158 s.trienodeHealSynced++ 2159 s.trienodeHealBytes += common.StorageSize(len(node)) 2160 2161 err := s.healer.scheduler.ProcessNode(trie.NodeSyncResult{Path: res.paths[i], Data: node}) 2162 switch err { 2163 case nil: 2164 case trie.ErrAlreadyProcessed: 2165 s.trienodeHealDups++ 2166 case trie.ErrNotRequested: 2167 s.trienodeHealNops++ 2168 default: 2169 log.Error("Invalid trienode processed", "hash", hash, "err", err) 2170 } 2171 } 2172 s.commitHealer(false) 2173 2174 // Calculate the processing rate of one filled trie node 2175 rate := float64(fills) / (float64(time.Since(start)) / float64(time.Second)) 2176 2177 // Update the currently measured trienode queueing and processing throughput. 2178 // 2179 // The processing rate needs to be updated uniformly independent if we've 2180 // processed 1x100 trie nodes or 100x1 to keep the rate consistent even in 2181 // the face of varying network packets. As such, we cannot just measure the 2182 // time it took to process N trie nodes and update once, we need one update 2183 // per trie node. 2184 // 2185 // Naively, that would be: 2186 // 2187 // for i:=0; i<fills; i++ { 2188 // healRate = (1-measurementImpact)*oldRate + measurementImpact*newRate 2189 // } 2190 // 2191 // Essentially, a recursive expansion of HR = (1-MI)*HR + MI*NR. 2192 // 2193 // We can expand that formula for the Nth item as: 2194 // HR(N) = (1-MI)^N*OR + (1-MI)^(N-1)*MI*NR + (1-MI)^(N-2)*MI*NR + ... + (1-MI)^0*MI*NR 2195 // 2196 // The above is a geometric sequence that can be summed to: 2197 // HR(N) = (1-MI)^N*(OR-NR) + NR 2198 s.trienodeHealRate = gomath.Pow(1-trienodeHealRateMeasurementImpact, float64(fills))*(s.trienodeHealRate-rate) + rate 2199 2200 pending := atomic.LoadUint64(&s.trienodeHealPend) 2201 if time.Since(s.trienodeHealThrottled) > time.Second { 2202 // Periodically adjust the trie node throttler 2203 if float64(pending) > 2*s.trienodeHealRate { 2204 s.trienodeHealThrottle *= trienodeHealThrottleIncrease 2205 } else { 2206 s.trienodeHealThrottle /= trienodeHealThrottleDecrease 2207 } 2208 if s.trienodeHealThrottle > maxTrienodeHealThrottle { 2209 s.trienodeHealThrottle = maxTrienodeHealThrottle 2210 } else if s.trienodeHealThrottle < minTrienodeHealThrottle { 2211 s.trienodeHealThrottle = minTrienodeHealThrottle 2212 } 2213 s.trienodeHealThrottled = time.Now() 2214 2215 log.Debug("Updated trie node heal throttler", "rate", s.trienodeHealRate, "pending", pending, "throttle", s.trienodeHealThrottle) 2216 } 2217 } 2218 2219 func (s *Syncer) commitHealer(force bool) { 2220 if !force && s.healer.scheduler.MemSize() < ethdb.IdealBatchSize { 2221 return 2222 } 2223 batch := s.db.NewBatch() 2224 if err := s.healer.scheduler.Commit(batch); err != nil { 2225 log.Error("Failed to commit healing data", "err", err) 2226 } 2227 if err := batch.Write(); err != nil { 2228 log.Crit("Failed to persist healing data", "err", err) 2229 } 2230 log.Debug("Persisted set of healing data", "type", "trienodes", "bytes", common.StorageSize(batch.ValueSize())) 2231 } 2232 2233 // processBytecodeHealResponse integrates an already validated bytecode response 2234 // into the healer tasks. 2235 func (s *Syncer) processBytecodeHealResponse(res *bytecodeHealResponse) { 2236 for i, hash := range res.hashes { 2237 node := res.codes[i] 2238 2239 // If the trie node was not delivered, reschedule it 2240 if node == nil { 2241 res.task.codeTasks[hash] = struct{}{} 2242 continue 2243 } 2244 // Push the trie node into the state syncer 2245 s.bytecodeHealSynced++ 2246 s.bytecodeHealBytes += common.StorageSize(len(node)) 2247 2248 err := s.healer.scheduler.ProcessCode(trie.CodeSyncResult{Hash: hash, Data: node}) 2249 switch err { 2250 case nil: 2251 case trie.ErrAlreadyProcessed: 2252 s.bytecodeHealDups++ 2253 case trie.ErrNotRequested: 2254 s.bytecodeHealNops++ 2255 default: 2256 log.Error("Invalid bytecode processed", "hash", hash, "err", err) 2257 } 2258 } 2259 s.commitHealer(false) 2260 } 2261 2262 // forwardAccountTask takes a filled account task and persists anything available 2263 // into the database, after which it forwards the next account marker so that the 2264 // task's next chunk may be filled. 2265 func (s *Syncer) forwardAccountTask(task *accountTask) { 2266 // Remove any pending delivery 2267 res := task.res 2268 if res == nil { 2269 return // nothing to forward 2270 } 2271 task.res = nil 2272 2273 // Persist the received account segments. These flat state maybe 2274 // outdated during the sync, but it can be fixed later during the 2275 // snapshot generation. 2276 oldAccountBytes := s.accountBytes 2277 2278 batch := ethdb.HookedBatch{ 2279 Batch: s.db.NewBatch(), 2280 OnPut: func(key []byte, value []byte) { 2281 s.accountBytes += common.StorageSize(len(key) + len(value)) 2282 }, 2283 } 2284 for i, hash := range res.hashes { 2285 if task.needCode[i] || task.needState[i] { 2286 break 2287 } 2288 slim := snapshot.SlimAccountRLP(res.accounts[i].Nonce, res.accounts[i].Balance, res.accounts[i].Root, res.accounts[i].CodeHash) 2289 rawdb.WriteAccountSnapshot(batch, hash, slim) 2290 2291 // If the task is complete, drop it into the stack trie to generate 2292 // account trie nodes for it 2293 if !task.needHeal[i] { 2294 full, err := snapshot.FullAccountRLP(slim) // TODO(karalabe): Slim parsing can be omitted 2295 if err != nil { 2296 panic(err) // Really shouldn't ever happen 2297 } 2298 task.genTrie.Update(hash[:], full) 2299 } 2300 } 2301 // Flush anything written just now and update the stats 2302 if err := batch.Write(); err != nil { 2303 log.Crit("Failed to persist accounts", "err", err) 2304 } 2305 s.accountSynced += uint64(len(res.accounts)) 2306 2307 // Task filling persisted, push it the chunk marker forward to the first 2308 // account still missing data. 2309 for i, hash := range res.hashes { 2310 if task.needCode[i] || task.needState[i] { 2311 return 2312 } 2313 task.Next = incHash(hash) 2314 } 2315 // All accounts marked as complete, track if the entire task is done 2316 task.done = !res.cont 2317 2318 // Stack trie could have generated trie nodes, push them to disk (we need to 2319 // flush after finalizing task.done. It's fine even if we crash and lose this 2320 // write as it will only cause more data to be downloaded during heal. 2321 if task.done { 2322 if _, err := task.genTrie.Commit(); err != nil { 2323 log.Error("Failed to commit stack account", "err", err) 2324 } 2325 } 2326 if task.genBatch.ValueSize() > ethdb.IdealBatchSize || task.done { 2327 if err := task.genBatch.Write(); err != nil { 2328 log.Error("Failed to persist stack account", "err", err) 2329 } 2330 task.genBatch.Reset() 2331 } 2332 log.Debug("Persisted range of accounts", "accounts", len(res.accounts), "bytes", s.accountBytes-oldAccountBytes) 2333 } 2334 2335 // OnAccounts is a callback method to invoke when a range of accounts are 2336 // received from a remote peer. 2337 func (s *Syncer) OnAccounts(peer SyncPeer, id uint64, hashes []common.Hash, accounts [][]byte, proof [][]byte) error { 2338 size := common.StorageSize(len(hashes) * common.HashLength) 2339 for _, account := range accounts { 2340 size += common.StorageSize(len(account)) 2341 } 2342 for _, node := range proof { 2343 size += common.StorageSize(len(node)) 2344 } 2345 logger := peer.Log().New("reqid", id) 2346 logger.Trace("Delivering range of accounts", "hashes", len(hashes), "accounts", len(accounts), "proofs", len(proof), "bytes", size) 2347 2348 // Whether or not the response is valid, we can mark the peer as idle and 2349 // notify the scheduler to assign a new task. If the response is invalid, 2350 // we'll drop the peer in a bit. 2351 defer func() { 2352 s.lock.Lock() 2353 defer s.lock.Unlock() 2354 if _, ok := s.peers[peer.ID()]; ok { 2355 s.accountIdlers[peer.ID()] = struct{}{} 2356 } 2357 select { 2358 case s.update <- struct{}{}: 2359 default: 2360 } 2361 }() 2362 s.lock.Lock() 2363 // Ensure the response is for a valid request 2364 req, ok := s.accountReqs[id] 2365 if !ok { 2366 // Request stale, perhaps the peer timed out but came through in the end 2367 logger.Warn("Unexpected account range packet") 2368 s.lock.Unlock() 2369 return nil 2370 } 2371 delete(s.accountReqs, id) 2372 s.rates.Update(peer.ID(), AccountRangeMsg, time.Since(req.time), int(size)) 2373 2374 // Clean up the request timeout timer, we'll see how to proceed further based 2375 // on the actual delivered content 2376 if !req.timeout.Stop() { 2377 // The timeout is already triggered, and this request will be reverted+rescheduled 2378 s.lock.Unlock() 2379 return nil 2380 } 2381 // Response is valid, but check if peer is signalling that it does not have 2382 // the requested data. For account range queries that means the state being 2383 // retrieved was either already pruned remotely, or the peer is not yet 2384 // synced to our head. 2385 if len(hashes) == 0 && len(accounts) == 0 && len(proof) == 0 { 2386 logger.Debug("Peer rejected account range request", "root", s.root) 2387 s.statelessPeers[peer.ID()] = struct{}{} 2388 s.lock.Unlock() 2389 2390 // Signal this request as failed, and ready for rescheduling 2391 s.scheduleRevertAccountRequest(req) 2392 return nil 2393 } 2394 root := s.root 2395 s.lock.Unlock() 2396 2397 // Reconstruct a partial trie from the response and verify it 2398 keys := make([][]byte, len(hashes)) 2399 for i, key := range hashes { 2400 keys[i] = common.CopyBytes(key[:]) 2401 } 2402 nodes := make(light.NodeList, len(proof)) 2403 for i, node := range proof { 2404 nodes[i] = node 2405 } 2406 proofdb := nodes.NodeSet() 2407 2408 var end []byte 2409 if len(keys) > 0 { 2410 end = keys[len(keys)-1] 2411 } 2412 cont, err := trie.VerifyRangeProof(root, req.origin[:], end, keys, accounts, proofdb) 2413 if err != nil { 2414 logger.Warn("Account range failed proof", "err", err) 2415 // Signal this request as failed, and ready for rescheduling 2416 s.scheduleRevertAccountRequest(req) 2417 return err 2418 } 2419 accs := make([]*types.StateAccount, len(accounts)) 2420 for i, account := range accounts { 2421 acc := new(types.StateAccount) 2422 if err := rlp.DecodeBytes(account, acc); err != nil { 2423 panic(err) // We created these blobs, we must be able to decode them 2424 } 2425 accs[i] = acc 2426 } 2427 response := &accountResponse{ 2428 task: req.task, 2429 hashes: hashes, 2430 accounts: accs, 2431 cont: cont, 2432 } 2433 select { 2434 case req.deliver <- response: 2435 case <-req.cancel: 2436 case <-req.stale: 2437 } 2438 return nil 2439 } 2440 2441 // OnByteCodes is a callback method to invoke when a batch of contract 2442 // bytes codes are received from a remote peer. 2443 func (s *Syncer) OnByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) error { 2444 s.lock.RLock() 2445 syncing := !s.snapped 2446 s.lock.RUnlock() 2447 2448 if syncing { 2449 return s.onByteCodes(peer, id, bytecodes) 2450 } 2451 return s.onHealByteCodes(peer, id, bytecodes) 2452 } 2453 2454 // onByteCodes is a callback method to invoke when a batch of contract 2455 // bytes codes are received from a remote peer in the syncing phase. 2456 func (s *Syncer) onByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) error { 2457 var size common.StorageSize 2458 for _, code := range bytecodes { 2459 size += common.StorageSize(len(code)) 2460 } 2461 logger := peer.Log().New("reqid", id) 2462 logger.Trace("Delivering set of bytecodes", "bytecodes", len(bytecodes), "bytes", size) 2463 2464 // Whether or not the response is valid, we can mark the peer as idle and 2465 // notify the scheduler to assign a new task. If the response is invalid, 2466 // we'll drop the peer in a bit. 2467 defer func() { 2468 s.lock.Lock() 2469 defer s.lock.Unlock() 2470 if _, ok := s.peers[peer.ID()]; ok { 2471 s.bytecodeIdlers[peer.ID()] = struct{}{} 2472 } 2473 select { 2474 case s.update <- struct{}{}: 2475 default: 2476 } 2477 }() 2478 s.lock.Lock() 2479 // Ensure the response is for a valid request 2480 req, ok := s.bytecodeReqs[id] 2481 if !ok { 2482 // Request stale, perhaps the peer timed out but came through in the end 2483 logger.Warn("Unexpected bytecode packet") 2484 s.lock.Unlock() 2485 return nil 2486 } 2487 delete(s.bytecodeReqs, id) 2488 s.rates.Update(peer.ID(), ByteCodesMsg, time.Since(req.time), len(bytecodes)) 2489 2490 // Clean up the request timeout timer, we'll see how to proceed further based 2491 // on the actual delivered content 2492 if !req.timeout.Stop() { 2493 // The timeout is already triggered, and this request will be reverted+rescheduled 2494 s.lock.Unlock() 2495 return nil 2496 } 2497 2498 // Response is valid, but check if peer is signalling that it does not have 2499 // the requested data. For bytecode range queries that means the peer is not 2500 // yet synced. 2501 if len(bytecodes) == 0 { 2502 logger.Debug("Peer rejected bytecode request") 2503 s.statelessPeers[peer.ID()] = struct{}{} 2504 s.lock.Unlock() 2505 2506 // Signal this request as failed, and ready for rescheduling 2507 s.scheduleRevertBytecodeRequest(req) 2508 return nil 2509 } 2510 s.lock.Unlock() 2511 2512 // Cross reference the requested bytecodes with the response to find gaps 2513 // that the serving node is missing 2514 hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) 2515 hash := make([]byte, 32) 2516 2517 codes := make([][]byte, len(req.hashes)) 2518 for i, j := 0, 0; i < len(bytecodes); i++ { 2519 // Find the next hash that we've been served, leaving misses with nils 2520 hasher.Reset() 2521 hasher.Write(bytecodes[i]) 2522 hasher.Read(hash) 2523 2524 for j < len(req.hashes) && !bytes.Equal(hash, req.hashes[j][:]) { 2525 j++ 2526 } 2527 if j < len(req.hashes) { 2528 codes[j] = bytecodes[i] 2529 j++ 2530 continue 2531 } 2532 // We've either ran out of hashes, or got unrequested data 2533 logger.Warn("Unexpected bytecodes", "count", len(bytecodes)-i) 2534 // Signal this request as failed, and ready for rescheduling 2535 s.scheduleRevertBytecodeRequest(req) 2536 return errors.New("unexpected bytecode") 2537 } 2538 // Response validated, send it to the scheduler for filling 2539 response := &bytecodeResponse{ 2540 task: req.task, 2541 hashes: req.hashes, 2542 codes: codes, 2543 } 2544 select { 2545 case req.deliver <- response: 2546 case <-req.cancel: 2547 case <-req.stale: 2548 } 2549 return nil 2550 } 2551 2552 // OnStorage is a callback method to invoke when ranges of storage slots 2553 // are received from a remote peer. 2554 func (s *Syncer) OnStorage(peer SyncPeer, id uint64, hashes [][]common.Hash, slots [][][]byte, proof [][]byte) error { 2555 // Gather some trace stats to aid in debugging issues 2556 var ( 2557 hashCount int 2558 slotCount int 2559 size common.StorageSize 2560 ) 2561 for _, hashset := range hashes { 2562 size += common.StorageSize(common.HashLength * len(hashset)) 2563 hashCount += len(hashset) 2564 } 2565 for _, slotset := range slots { 2566 for _, slot := range slotset { 2567 size += common.StorageSize(len(slot)) 2568 } 2569 slotCount += len(slotset) 2570 } 2571 for _, node := range proof { 2572 size += common.StorageSize(len(node)) 2573 } 2574 logger := peer.Log().New("reqid", id) 2575 logger.Trace("Delivering ranges of storage slots", "accounts", len(hashes), "hashes", hashCount, "slots", slotCount, "proofs", len(proof), "size", size) 2576 2577 // Whether or not the response is valid, we can mark the peer as idle and 2578 // notify the scheduler to assign a new task. If the response is invalid, 2579 // we'll drop the peer in a bit. 2580 defer func() { 2581 s.lock.Lock() 2582 defer s.lock.Unlock() 2583 if _, ok := s.peers[peer.ID()]; ok { 2584 s.storageIdlers[peer.ID()] = struct{}{} 2585 } 2586 select { 2587 case s.update <- struct{}{}: 2588 default: 2589 } 2590 }() 2591 s.lock.Lock() 2592 // Ensure the response is for a valid request 2593 req, ok := s.storageReqs[id] 2594 if !ok { 2595 // Request stale, perhaps the peer timed out but came through in the end 2596 logger.Warn("Unexpected storage ranges packet") 2597 s.lock.Unlock() 2598 return nil 2599 } 2600 delete(s.storageReqs, id) 2601 s.rates.Update(peer.ID(), StorageRangesMsg, time.Since(req.time), int(size)) 2602 2603 // Clean up the request timeout timer, we'll see how to proceed further based 2604 // on the actual delivered content 2605 if !req.timeout.Stop() { 2606 // The timeout is already triggered, and this request will be reverted+rescheduled 2607 s.lock.Unlock() 2608 return nil 2609 } 2610 2611 // Reject the response if the hash sets and slot sets don't match, or if the 2612 // peer sent more data than requested. 2613 if len(hashes) != len(slots) { 2614 s.lock.Unlock() 2615 s.scheduleRevertStorageRequest(req) // reschedule request 2616 logger.Warn("Hash and slot set size mismatch", "hashset", len(hashes), "slotset", len(slots)) 2617 return errors.New("hash and slot set size mismatch") 2618 } 2619 if len(hashes) > len(req.accounts) { 2620 s.lock.Unlock() 2621 s.scheduleRevertStorageRequest(req) // reschedule request 2622 logger.Warn("Hash set larger than requested", "hashset", len(hashes), "requested", len(req.accounts)) 2623 return errors.New("hash set larger than requested") 2624 } 2625 // Response is valid, but check if peer is signalling that it does not have 2626 // the requested data. For storage range queries that means the state being 2627 // retrieved was either already pruned remotely, or the peer is not yet 2628 // synced to our head. 2629 if len(hashes) == 0 { 2630 logger.Debug("Peer rejected storage request") 2631 s.statelessPeers[peer.ID()] = struct{}{} 2632 s.lock.Unlock() 2633 s.scheduleRevertStorageRequest(req) // reschedule request 2634 return nil 2635 } 2636 s.lock.Unlock() 2637 2638 // Reconstruct the partial tries from the response and verify them 2639 var cont bool 2640 2641 for i := 0; i < len(hashes); i++ { 2642 // Convert the keys and proofs into an internal format 2643 keys := make([][]byte, len(hashes[i])) 2644 for j, key := range hashes[i] { 2645 keys[j] = common.CopyBytes(key[:]) 2646 } 2647 nodes := make(light.NodeList, 0, len(proof)) 2648 if i == len(hashes)-1 { 2649 for _, node := range proof { 2650 nodes = append(nodes, node) 2651 } 2652 } 2653 var err error 2654 if len(nodes) == 0 { 2655 // No proof has been attached, the response must cover the entire key 2656 // space and hash to the origin root. 2657 _, err = trie.VerifyRangeProof(req.roots[i], nil, nil, keys, slots[i], nil) 2658 if err != nil { 2659 s.scheduleRevertStorageRequest(req) // reschedule request 2660 logger.Warn("Storage slots failed proof", "err", err) 2661 return err 2662 } 2663 } else { 2664 // A proof was attached, the response is only partial, check that the 2665 // returned data is indeed part of the storage trie 2666 proofdb := nodes.NodeSet() 2667 2668 var end []byte 2669 if len(keys) > 0 { 2670 end = keys[len(keys)-1] 2671 } 2672 cont, err = trie.VerifyRangeProof(req.roots[i], req.origin[:], end, keys, slots[i], proofdb) 2673 if err != nil { 2674 s.scheduleRevertStorageRequest(req) // reschedule request 2675 logger.Warn("Storage range failed proof", "err", err) 2676 return err 2677 } 2678 } 2679 } 2680 // Partial tries reconstructed, send them to the scheduler for storage filling 2681 response := &storageResponse{ 2682 mainTask: req.mainTask, 2683 subTask: req.subTask, 2684 accounts: req.accounts, 2685 roots: req.roots, 2686 hashes: hashes, 2687 slots: slots, 2688 cont: cont, 2689 } 2690 select { 2691 case req.deliver <- response: 2692 case <-req.cancel: 2693 case <-req.stale: 2694 } 2695 return nil 2696 } 2697 2698 // OnTrieNodes is a callback method to invoke when a batch of trie nodes 2699 // are received from a remote peer. 2700 func (s *Syncer) OnTrieNodes(peer SyncPeer, id uint64, trienodes [][]byte) error { 2701 var size common.StorageSize 2702 for _, node := range trienodes { 2703 size += common.StorageSize(len(node)) 2704 } 2705 logger := peer.Log().New("reqid", id) 2706 logger.Trace("Delivering set of healing trienodes", "trienodes", len(trienodes), "bytes", size) 2707 2708 // Whether or not the response is valid, we can mark the peer as idle and 2709 // notify the scheduler to assign a new task. If the response is invalid, 2710 // we'll drop the peer in a bit. 2711 defer func() { 2712 s.lock.Lock() 2713 defer s.lock.Unlock() 2714 if _, ok := s.peers[peer.ID()]; ok { 2715 s.trienodeHealIdlers[peer.ID()] = struct{}{} 2716 } 2717 select { 2718 case s.update <- struct{}{}: 2719 default: 2720 } 2721 }() 2722 s.lock.Lock() 2723 // Ensure the response is for a valid request 2724 req, ok := s.trienodeHealReqs[id] 2725 if !ok { 2726 // Request stale, perhaps the peer timed out but came through in the end 2727 logger.Warn("Unexpected trienode heal packet") 2728 s.lock.Unlock() 2729 return nil 2730 } 2731 delete(s.trienodeHealReqs, id) 2732 s.rates.Update(peer.ID(), TrieNodesMsg, time.Since(req.time), len(trienodes)) 2733 2734 // Clean up the request timeout timer, we'll see how to proceed further based 2735 // on the actual delivered content 2736 if !req.timeout.Stop() { 2737 // The timeout is already triggered, and this request will be reverted+rescheduled 2738 s.lock.Unlock() 2739 return nil 2740 } 2741 2742 // Response is valid, but check if peer is signalling that it does not have 2743 // the requested data. For bytecode range queries that means the peer is not 2744 // yet synced. 2745 if len(trienodes) == 0 { 2746 logger.Debug("Peer rejected trienode heal request") 2747 s.statelessPeers[peer.ID()] = struct{}{} 2748 s.lock.Unlock() 2749 2750 // Signal this request as failed, and ready for rescheduling 2751 s.scheduleRevertTrienodeHealRequest(req) 2752 return nil 2753 } 2754 s.lock.Unlock() 2755 2756 // Cross reference the requested trienodes with the response to find gaps 2757 // that the serving node is missing 2758 var ( 2759 hasher = sha3.NewLegacyKeccak256().(crypto.KeccakState) 2760 hash = make([]byte, 32) 2761 nodes = make([][]byte, len(req.hashes)) 2762 fills uint64 2763 ) 2764 for i, j := 0, 0; i < len(trienodes); i++ { 2765 // Find the next hash that we've been served, leaving misses with nils 2766 hasher.Reset() 2767 hasher.Write(trienodes[i]) 2768 hasher.Read(hash) 2769 2770 for j < len(req.hashes) && !bytes.Equal(hash, req.hashes[j][:]) { 2771 j++ 2772 } 2773 if j < len(req.hashes) { 2774 nodes[j] = trienodes[i] 2775 fills++ 2776 j++ 2777 continue 2778 } 2779 // We've either ran out of hashes, or got unrequested data 2780 logger.Warn("Unexpected healing trienodes", "count", len(trienodes)-i) 2781 2782 // Signal this request as failed, and ready for rescheduling 2783 s.scheduleRevertTrienodeHealRequest(req) 2784 return errors.New("unexpected healing trienode") 2785 } 2786 // Response validated, send it to the scheduler for filling 2787 atomic.AddUint64(&s.trienodeHealPend, fills) 2788 defer func() { 2789 atomic.AddUint64(&s.trienodeHealPend, ^(fills - 1)) 2790 }() 2791 response := &trienodeHealResponse{ 2792 paths: req.paths, 2793 task: req.task, 2794 hashes: req.hashes, 2795 nodes: nodes, 2796 } 2797 select { 2798 case req.deliver <- response: 2799 case <-req.cancel: 2800 case <-req.stale: 2801 } 2802 return nil 2803 } 2804 2805 // onHealByteCodes is a callback method to invoke when a batch of contract 2806 // bytes codes are received from a remote peer in the healing phase. 2807 func (s *Syncer) onHealByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) error { 2808 var size common.StorageSize 2809 for _, code := range bytecodes { 2810 size += common.StorageSize(len(code)) 2811 } 2812 logger := peer.Log().New("reqid", id) 2813 logger.Trace("Delivering set of healing bytecodes", "bytecodes", len(bytecodes), "bytes", size) 2814 2815 // Whether or not the response is valid, we can mark the peer as idle and 2816 // notify the scheduler to assign a new task. If the response is invalid, 2817 // we'll drop the peer in a bit. 2818 defer func() { 2819 s.lock.Lock() 2820 defer s.lock.Unlock() 2821 if _, ok := s.peers[peer.ID()]; ok { 2822 s.bytecodeHealIdlers[peer.ID()] = struct{}{} 2823 } 2824 select { 2825 case s.update <- struct{}{}: 2826 default: 2827 } 2828 }() 2829 s.lock.Lock() 2830 // Ensure the response is for a valid request 2831 req, ok := s.bytecodeHealReqs[id] 2832 if !ok { 2833 // Request stale, perhaps the peer timed out but came through in the end 2834 logger.Warn("Unexpected bytecode heal packet") 2835 s.lock.Unlock() 2836 return nil 2837 } 2838 delete(s.bytecodeHealReqs, id) 2839 s.rates.Update(peer.ID(), ByteCodesMsg, time.Since(req.time), len(bytecodes)) 2840 2841 // Clean up the request timeout timer, we'll see how to proceed further based 2842 // on the actual delivered content 2843 if !req.timeout.Stop() { 2844 // The timeout is already triggered, and this request will be reverted+rescheduled 2845 s.lock.Unlock() 2846 return nil 2847 } 2848 2849 // Response is valid, but check if peer is signalling that it does not have 2850 // the requested data. For bytecode range queries that means the peer is not 2851 // yet synced. 2852 if len(bytecodes) == 0 { 2853 logger.Debug("Peer rejected bytecode heal request") 2854 s.statelessPeers[peer.ID()] = struct{}{} 2855 s.lock.Unlock() 2856 2857 // Signal this request as failed, and ready for rescheduling 2858 s.scheduleRevertBytecodeHealRequest(req) 2859 return nil 2860 } 2861 s.lock.Unlock() 2862 2863 // Cross reference the requested bytecodes with the response to find gaps 2864 // that the serving node is missing 2865 hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) 2866 hash := make([]byte, 32) 2867 2868 codes := make([][]byte, len(req.hashes)) 2869 for i, j := 0, 0; i < len(bytecodes); i++ { 2870 // Find the next hash that we've been served, leaving misses with nils 2871 hasher.Reset() 2872 hasher.Write(bytecodes[i]) 2873 hasher.Read(hash) 2874 2875 for j < len(req.hashes) && !bytes.Equal(hash, req.hashes[j][:]) { 2876 j++ 2877 } 2878 if j < len(req.hashes) { 2879 codes[j] = bytecodes[i] 2880 j++ 2881 continue 2882 } 2883 // We've either ran out of hashes, or got unrequested data 2884 logger.Warn("Unexpected healing bytecodes", "count", len(bytecodes)-i) 2885 // Signal this request as failed, and ready for rescheduling 2886 s.scheduleRevertBytecodeHealRequest(req) 2887 return errors.New("unexpected healing bytecode") 2888 } 2889 // Response validated, send it to the scheduler for filling 2890 response := &bytecodeHealResponse{ 2891 task: req.task, 2892 hashes: req.hashes, 2893 codes: codes, 2894 } 2895 select { 2896 case req.deliver <- response: 2897 case <-req.cancel: 2898 case <-req.stale: 2899 } 2900 return nil 2901 } 2902 2903 // onHealState is a callback method to invoke when a flat state(account 2904 // or storage slot) is downloaded during the healing stage. The flat states 2905 // can be persisted blindly and can be fixed later in the generation stage. 2906 // Note it's not concurrent safe, please handle the concurrent issue outside. 2907 func (s *Syncer) onHealState(paths [][]byte, value []byte) error { 2908 if len(paths) == 1 { 2909 var account types.StateAccount 2910 if err := rlp.DecodeBytes(value, &account); err != nil { 2911 return nil // Returning the error here would drop the remote peer 2912 } 2913 blob := snapshot.SlimAccountRLP(account.Nonce, account.Balance, account.Root, account.CodeHash) 2914 rawdb.WriteAccountSnapshot(s.stateWriter, common.BytesToHash(paths[0]), blob) 2915 s.accountHealed += 1 2916 s.accountHealedBytes += common.StorageSize(1 + common.HashLength + len(blob)) 2917 } 2918 if len(paths) == 2 { 2919 rawdb.WriteStorageSnapshot(s.stateWriter, common.BytesToHash(paths[0]), common.BytesToHash(paths[1]), value) 2920 s.storageHealed += 1 2921 s.storageHealedBytes += common.StorageSize(1 + 2*common.HashLength + len(value)) 2922 } 2923 if s.stateWriter.ValueSize() > ethdb.IdealBatchSize { 2924 s.stateWriter.Write() // It's fine to ignore the error here 2925 s.stateWriter.Reset() 2926 } 2927 return nil 2928 } 2929 2930 // hashSpace is the total size of the 256 bit hash space for accounts. 2931 var hashSpace = new(big.Int).Exp(common.Big2, common.Big256, nil) 2932 2933 // report calculates various status reports and provides it to the user. 2934 func (s *Syncer) report(force bool) { 2935 if len(s.tasks) > 0 { 2936 s.reportSyncProgress(force) 2937 return 2938 } 2939 s.reportHealProgress(force) 2940 } 2941 2942 // reportSyncProgress calculates various status reports and provides it to the user. 2943 func (s *Syncer) reportSyncProgress(force bool) { 2944 // Don't report all the events, just occasionally 2945 if !force && time.Since(s.logTime) < 8*time.Second { 2946 return 2947 } 2948 // Don't report anything until we have a meaningful progress 2949 synced := s.accountBytes + s.bytecodeBytes + s.storageBytes 2950 if synced == 0 { 2951 return 2952 } 2953 accountGaps := new(big.Int) 2954 for _, task := range s.tasks { 2955 accountGaps.Add(accountGaps, new(big.Int).Sub(task.Last.Big(), task.Next.Big())) 2956 } 2957 accountFills := new(big.Int).Sub(hashSpace, accountGaps) 2958 if accountFills.BitLen() == 0 { 2959 return 2960 } 2961 s.logTime = time.Now() 2962 estBytes := float64(new(big.Int).Div( 2963 new(big.Int).Mul(new(big.Int).SetUint64(uint64(synced)), hashSpace), 2964 accountFills, 2965 ).Uint64()) 2966 // Don't report anything until we have a meaningful progress 2967 if estBytes < 1.0 { 2968 return 2969 } 2970 elapsed := time.Since(s.startTime) 2971 estTime := elapsed / time.Duration(synced) * time.Duration(estBytes) 2972 2973 // Create a mega progress report 2974 var ( 2975 progress = fmt.Sprintf("%.2f%%", float64(synced)*100/estBytes) 2976 accounts = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(s.accountSynced), s.accountBytes.TerminalString()) 2977 storage = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(s.storageSynced), s.storageBytes.TerminalString()) 2978 bytecode = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(s.bytecodeSynced), s.bytecodeBytes.TerminalString()) 2979 ) 2980 log.Info("Syncing: state download in progress", "synced", progress, "state", synced, 2981 "accounts", accounts, "slots", storage, "codes", bytecode, "eta", common.PrettyDuration(estTime-elapsed)) 2982 } 2983 2984 // reportHealProgress calculates various status reports and provides it to the user. 2985 func (s *Syncer) reportHealProgress(force bool) { 2986 // Don't report all the events, just occasionally 2987 if !force && time.Since(s.logTime) < 8*time.Second { 2988 return 2989 } 2990 s.logTime = time.Now() 2991 2992 // Create a mega progress report 2993 var ( 2994 trienode = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(s.trienodeHealSynced), s.trienodeHealBytes.TerminalString()) 2995 bytecode = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(s.bytecodeHealSynced), s.bytecodeHealBytes.TerminalString()) 2996 accounts = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(s.accountHealed), s.accountHealedBytes.TerminalString()) 2997 storage = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(s.storageHealed), s.storageHealedBytes.TerminalString()) 2998 ) 2999 log.Info("Syncing: state healing in progress", "accounts", accounts, "slots", storage, 3000 "codes", bytecode, "nodes", trienode, "pending", s.healer.scheduler.Pending()) 3001 } 3002 3003 // estimateRemainingSlots tries to determine roughly how many slots are left in 3004 // a contract storage, based on the number of keys and the last hash. This method 3005 // assumes that the hashes are lexicographically ordered and evenly distributed. 3006 func estimateRemainingSlots(hashes int, last common.Hash) (uint64, error) { 3007 if last == (common.Hash{}) { 3008 return 0, errors.New("last hash empty") 3009 } 3010 space := new(big.Int).Mul(math.MaxBig256, big.NewInt(int64(hashes))) 3011 space.Div(space, last.Big()) 3012 if !space.IsUint64() { 3013 // Gigantic address space probably due to too few or malicious slots 3014 return 0, errors.New("too few slots for estimation") 3015 } 3016 return space.Uint64() - uint64(hashes), nil 3017 } 3018 3019 // capacitySort implements the Sort interface, allowing sorting by peer message 3020 // throughput. Note, callers should use sort.Reverse to get the desired effect 3021 // of highest capacity being at the front. 3022 type capacitySort struct { 3023 ids []string 3024 caps []int 3025 } 3026 3027 func (s *capacitySort) Len() int { 3028 return len(s.ids) 3029 } 3030 3031 func (s *capacitySort) Less(i, j int) bool { 3032 return s.caps[i] < s.caps[j] 3033 } 3034 3035 func (s *capacitySort) Swap(i, j int) { 3036 s.ids[i], s.ids[j] = s.ids[j], s.ids[i] 3037 s.caps[i], s.caps[j] = s.caps[j], s.caps[i] 3038 } 3039 3040 // healRequestSort implements the Sort interface, allowing sorting trienode 3041 // heal requests, which is a prerequisite for merging storage-requests. 3042 type healRequestSort struct { 3043 paths []string 3044 hashes []common.Hash 3045 syncPaths []trie.SyncPath 3046 } 3047 3048 func (t *healRequestSort) Len() int { 3049 return len(t.hashes) 3050 } 3051 3052 func (t *healRequestSort) Less(i, j int) bool { 3053 a := t.syncPaths[i] 3054 b := t.syncPaths[j] 3055 switch bytes.Compare(a[0], b[0]) { 3056 case -1: 3057 return true 3058 case 1: 3059 return false 3060 } 3061 // identical first part 3062 if len(a) < len(b) { 3063 return true 3064 } 3065 if len(b) < len(a) { 3066 return false 3067 } 3068 if len(a) == 2 { 3069 return bytes.Compare(a[1], b[1]) < 0 3070 } 3071 return false 3072 } 3073 3074 func (t *healRequestSort) Swap(i, j int) { 3075 t.paths[i], t.paths[j] = t.paths[j], t.paths[i] 3076 t.hashes[i], t.hashes[j] = t.hashes[j], t.hashes[i] 3077 t.syncPaths[i], t.syncPaths[j] = t.syncPaths[j], t.syncPaths[i] 3078 } 3079 3080 // Merge merges the pathsets, so that several storage requests concerning the 3081 // same account are merged into one, to reduce bandwidth. 3082 // OBS: This operation is moot if t has not first been sorted. 3083 func (t *healRequestSort) Merge() []TrieNodePathSet { 3084 var result []TrieNodePathSet 3085 for _, path := range t.syncPaths { 3086 pathset := TrieNodePathSet([][]byte(path)) 3087 if len(path) == 1 { 3088 // It's an account reference. 3089 result = append(result, pathset) 3090 } else { 3091 // It's a storage reference. 3092 end := len(result) - 1 3093 if len(result) == 0 || !bytes.Equal(pathset[0], result[end][0]) { 3094 // The account doesn't match last, create a new entry. 3095 result = append(result, pathset) 3096 } else { 3097 // It's the same account as the previous one, add to the storage 3098 // paths of that request. 3099 result[end] = append(result[end], pathset[1]) 3100 } 3101 } 3102 } 3103 return result 3104 } 3105 3106 // sortByAccountPath takes hashes and paths, and sorts them. After that, it generates 3107 // the TrieNodePaths and merges paths which belongs to the same account path. 3108 func sortByAccountPath(paths []string, hashes []common.Hash) ([]string, []common.Hash, []trie.SyncPath, []TrieNodePathSet) { 3109 var syncPaths []trie.SyncPath 3110 for _, path := range paths { 3111 syncPaths = append(syncPaths, trie.NewSyncPath([]byte(path))) 3112 } 3113 n := &healRequestSort{paths, hashes, syncPaths} 3114 sort.Sort(n) 3115 pathsets := n.Merge() 3116 return n.paths, n.hashes, n.syncPaths, pathsets 3117 }