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