forked from ethereum/go-ethereum
-
Notifications
You must be signed in to change notification settings - Fork 0
/
blockchain.go
1828 lines (1643 loc) · 73.6 KB
/
blockchain.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package core implements the Ethereum consensus protocol.
// 코어패키지는 이더리움 합의 프로토콜을 구현한다
package core
import (
"errors"
"fmt"
"io"
"math/big"
mrand "math/rand"
"sync"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/hashicorp/golang-lru"
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
)
var (
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
ErrNoGenesis = errors.New("Genesis not found in chain")
)
const (
bodyCacheLimit = 256
blockCacheLimit = 256
maxFutureBlocks = 256
maxTimeFutureBlocks = 30
badBlockLimit = 10
triesInMemory = 128
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
// BlockChainVersion은 연동불가능한 DB를 scratch부터 새롭게 싱크하도록 강제한다
BlockChainVersion = 3
)
// CacheConfig contains the configuration values for the trie caching/pruning
// that's resident in a blockchain.
// CacheConfig 구조체는 블록체인에 있는 trie의 캐싱/가지치기를 위한 설정값을 포함한다
type CacheConfig struct {
Disabled bool // Whether to disable trie write caching (archive node)
// trie쓰기 캐싱의 on/off여부
TrieNodeLimit int // Memory limit (MB) at which to flush the current in-memory trie to disk
// 메모리 상의 트라이를 디스크에쓰게하는 메모리 한도
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
// 메모리 상의 트라이를 디스크에쓰게하는 시간 한도
}
// BlockChain represents the canonical chain given a database with a genesis
// block. The Blockchain manages chain imports, reverts, chain reorganisations.
//
// Importing blocks in to the block chain happens according to the set of rules
// defined by the two stage Validator. Processing of blocks is done using the
// Processor which processes the included transaction. The validation of the state
// is done in the second part of the Validator. Failing results in aborting of
// the import.
//
// The BlockChain also helps in returning blocks from **any** chain included
// in the database as well as blocks that represents the canonical chain. It's
// important to note that GetBlock can return any block and does not need to be
// included in the canonical one where as GetBlockByNumber always represents the
// canonical chain.
// 블록체인 구조체는 제네시스 블록을 가진 주어진 DB의 캐노니컬 체인을 표현한다.
// 체인의 수신/되돌리기/재구성 작업을 관리한다
// 블록을 수신하는 것은 두개의 스테이지 검증자의 규칙세트에 따라 발생한다.
// 블록들의 처리는 포함된 트렌젝션을 처리하는 state 처리자를 이용하여 완료한다.
// state 검증은 검증자의 두번째 스테이지에서 끝나고, 검증 실패시 수신이 포기된다
// 이구조체는 db상의 어떤 canonical체인도 지원한다.
// 이부분이 중요한 이유는 GetBlocks이 캐노니컬 채인에 필요하지 안은 블록을 리턴할 가능성이 있으며
// 캐노니컬안에 포함될 필요가 없다. 왜냐하면 GetBlockByNumber가 언제나 캐노니컬 체인을 나타내기 때문이다
type BlockChain struct {
chainConfig *params.ChainConfig // Chain & network configuration
// 체인과 네트워크 설정
cacheConfig *CacheConfig // Cache configuration for pruning
// 가지치기를 위한 캐시 설정
db ethdb.Database // Low level persistent database to store final content in
// 최종 컨텐츠를 저장할 저수준의 persistent한 DB
triegc *prque.Prque // Priority queue mapping block numbers to tries to gc
// 가비지 컬렉션을 위한 쁠록넘버가 trie에 매핑된 우선순위 큐
gcproc time.Duration // Accumulates canonical block processing for trie dumping
// 트라이 덤프를 위해 캐노니컬 블록을 처리한 누적시간
hc *HeaderChain
rmLogsFeed event.Feed
chainFeed event.Feed
chainSideFeed event.Feed
chainHeadFeed event.Feed
logsFeed event.Feed
scope event.SubscriptionScope
genesisBlock *types.Block
mu sync.RWMutex // global mutex for locking chain operations
// 체인 Lock을 위한 전역 mutex
chainmu sync.RWMutex // blockchain insertion lock
// 블록체인 삽입 lock
procmu sync.RWMutex // block processor lock
// 블록 처리 lock
checkpoint int // checkpoint counts towards the new checkpoint
// 새로운 체크포인트를 위한 체크포인트 카운트
currentBlock atomic.Value // Current head of the block chain
// 블록체인의 현재 헤드
currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
// fast-sync 체인의 현재 헤드(대부분 블록체인보다 앞선다)
stateCache state.Database // State database to reuse between imports (contains state cache)
// 블록 입수간 재사용될 스테이트 캐시를 포함하는 StateDB
bodyCache *lru.Cache // Cache for the most recent block bodies
// 최근 블록의 바디들를 위한 캐시
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
// 최근 블록의 RLP 인코딩된 바디들
blockCache *lru.Cache // Cache for the most recent entire blocks
// 최근 블록들의 캐시
futureBlocks *lru.Cache // future blocks are blocks added for later processing
// 나중에 처리되기위해 더해진 블록들
quit chan struct{} // blockchain quit channel
// 블록체인 종료채널
running int32 // running must be called atomically
// atomic하게 호출되어야하는 running
// procInterrupt must be atomically called
procInterrupt int32 // interrupt signaler for block processing
// 블록처리를 위한 인터럽트 시그널
wg sync.WaitGroup // chain processing wait group for shutting down
// 종료를 위한 채인처리 대기 그룹
engine consensus.Engine
processor Processor // block processor interface
// 블록 처리자 인터페이스
validator Validator // block and state validator interface
// 블록과 상태 검증자 인터페이스
vmConfig vm.Config
badBlocks *lru.Cache // Bad block cache
// 배드블록 캐시
}
// NewBlockChain returns a fully initialised block chain using information
// available in the database. It initialises the default Ethereum Validator and
// Processor.
// 이 함수는 DB의 정보를 이용해서 완전히 초기화된 블록체인을 리턴한다.
// 이더리움의 기본 검증자와 처리자를 초기화한다
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {
if cacheConfig == nil {
cacheConfig = &CacheConfig{
TrieNodeLimit: 256 * 1024 * 1024,
TrieTimeLimit: 5 * time.Minute,
}
}
bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit)
blockCache, _ := lru.New(blockCacheLimit)
futureBlocks, _ := lru.New(maxFutureBlocks)
badBlocks, _ := lru.New(badBlockLimit)
bc := &BlockChain{
chainConfig: chainConfig,
cacheConfig: cacheConfig,
db: db,
triegc: prque.New(),
stateCache: state.NewDatabase(db),
quit: make(chan struct{}),
bodyCache: bodyCache,
bodyRLPCache: bodyRLPCache,
blockCache: blockCache,
futureBlocks: futureBlocks,
engine: engine,
vmConfig: vmConfig,
badBlocks: badBlocks,
}
// 검증자와 스테이트 처리자 설정
// 블록 검증자는 블록의 헤더와 엉클블락들과 처리된 스테이트를 검증해야한다
// 상태 처리자는 기본 처리자로서, 한 지점에서 다른 지점으로의 state의 변환을 관리한다
bc.SetValidator(NewBlockValidator(chainConfig, bc, engine))
bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine))
var err error
// 이 함수는 새로운 헤더체인 구조체를 생성한다.
bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.getProcInterrupt)
if err != nil {
return nil, err
}
bc.genesisBlock = bc.GetBlockByNumber(0)
if bc.genesisBlock == nil {
return nil, ErrNoGenesis
}
// 이함수는 DB로부터 마지막으로 알려진 state를 읽어온다.
// 메인 계정 trie를 오픈하고 stateDB를 생성한다
// 현재 블록과 현재 블록헤더를 설정하고 tota difficulty를 계산한다
if err := bc.loadLastState(); err != nil {
return nil, err
}
// Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain
// 현재 블록 해시들의 상태를 체크하고 배드블럭들이 체인에 없는지 체크한다
for hash := range BadHashes {
if header := bc.GetHeaderByHash(hash); header != nil {
// get the canonical block corresponding to the offending header's number
// 문제가되는 헤더의 번호와 관련된 캐노니컬 블록을 찾는다
headerByNumber := bc.GetHeaderByNumber(header.Number.Uint64())
// make sure the headerByNumber (if present) is in our current canonical chain
// 나타난다면 넘버에의한 헤더가 우리의 캐노니컬 체인에 있는지 확인한다
if headerByNumber != nil && headerByNumber.Hash() == header.Hash() {
log.Error("Found bad hash, rewinding chain", "number", header.Number, "hash", header.ParentHash)
bc.SetHead(header.Number.Uint64() - 1)
log.Error("Chain rewind was successful, resuming normal operation")
}
}
}
// Take ownership of this particular state
// 5초마다 퓨처블록들을 체인에 추가하는 루틴 실행
go bc.update()
return bc, nil
}
func (bc *BlockChain) getProcInterrupt() bool {
return atomic.LoadInt32(&bc.procInterrupt) == 1
}
// loadLastState loads the last known chain state from the database. This method
// assumes that the chain manager mutex is held.
// loadLastState 함수는 DB로부터 마지막으로 알려진 state를 읽어온다.
func (bc *BlockChain) loadLastState() error {
// Restore the last known head block
// 마지막으로 알려진 헤드블록을 복원한다
head := rawdb.ReadHeadBlockHash(bc.db)
if head == (common.Hash{}) {
// Corrupt or empty database, init from scratch
// 망가졌거나 빈 DB. 스크래치로부터 초기화한다
log.Warn("Empty database, resetting chain")
return bc.Reset()
}
// Make sure the entire head block is available
//헤더블럭을 검증한다
currentBlock := bc.GetBlockByHash(head)
if currentBlock == nil {
// Corrupt or empty database, init from scratch
// 망가졌거나 빈 DB. 스크래치로부터 초기화한다
log.Warn("Head block missing, resetting chain", "hash", head)
return bc.Reset()
}
// Make sure the state associated with the block is available
// 블록과 관련된 상태가 온전한지 확인
// 메인 계정 trie를 오픈하고 stateDB를 생성한다
if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
// Dangling block without a state associated, init from scratch
// 상태에 연관없는 블록은 초기화한다
log.Warn("Head state missing, repairing chain", "number", currentBlock.Number(), "hash", currentBlock.Hash())
if err := bc.repair(¤tBlock); err != nil {
return err
}
}
// Everything seems to be fine, set as the head block
// 문제가 없다면 헤드블록으로 설정한다
bc.currentBlock.Store(currentBlock)
// Restore the last known head header
// 현재블록으로 저장하고 현재 head값을 하나 증가시켜 common.hash값을 가지게 한다.
// 아까 전엔 현재 블록 헤드가 common hash를 가지고 있었다.
currentHeader := currentBlock.Header()
if head := rawdb.ReadHeadHeaderHash(bc.db); head != (common.Hash{}) {
if header := bc.GetHeaderByHash(head); header != nil {
currentHeader = header
}
}
//current head 는 불록의 다음 헤드를 가리킨다.
bc.hc.SetCurrentHeader(currentHeader)
// Restore the last known head fast block
// 빠른 블록에 현재 블록을 설정한다
bc.currentFastBlock.Store(currentBlock)
if head := rawdb.ReadHeadFastBlockHash(bc.db); head != (common.Hash{}) {
if block := bc.GetBlockByHash(head); block != nil {
bc.currentFastBlock.Store(block)
}
}
// Issue a status log for the user
// 사용자를 위한 상태로그를 이슈잉한다
currentFastBlock := bc.CurrentFastBlock()
//Total difficulty 업데이트
headerTd := bc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
blockTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
fastTd := bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64())
log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd)
log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd)
log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd)
return nil
}
// SetHead rewinds the local chain to a new head. In the case of headers, everything
// above the new head will be deleted and the new one set. In the case of blocks
// though, the head may be further rewound if block bodies are missing (non-archive
// nodes after a fast sync).
// 이 함수는 로컬체인을 새로운 해드로 되돌린다.
// 헤더들의 경우 뉴 헤드 위쪽의 모든 헤더는 모두 삭제되고 새로운 헤더가 설정될 것이다.
// 블록의 경우, 블록 바디가 없을 경우 좀더 되돌려진다.
func (bc *BlockChain) SetHead(head uint64) error {
log.Warn("Rewinding blockchain", "target", head)
bc.mu.Lock()
defer bc.mu.Unlock()
// Rewind the header chain, deleting all block bodies until then
// 헤더체인을 되돌린다. 해당 시점까지의 바디를 모두 삭제한다
delFn := func(hash common.Hash, num uint64) {
rawdb.DeleteBody(bc.db, hash, num)
}
bc.hc.SetHead(head, delFn)
currentHeader := bc.hc.CurrentHeader()
// Clear out any stale content from the caches
// 캐시로부터 대기중인 컨텐츠를 삭제한다
bc.bodyCache.Purge()
bc.bodyRLPCache.Purge()
bc.blockCache.Purge()
bc.futureBlocks.Purge()
// Rewind the block chain, ensuring we don't end up with a stateless head block
// 블록체인을 되돌린다, 상태가 없는 헤드블록으로 끝나지 않음을 보장한다
if currentBlock := bc.CurrentBlock(); currentBlock != nil && currentHeader.Number.Uint64() < currentBlock.NumberU64() {
bc.currentBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
}
if currentBlock := bc.CurrentBlock(); currentBlock != nil {
if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
// Rewound state missing, rolled back to before pivot, reset to genesis
// 되돌아갈 상태가 없음. 피봇으로 롤벡하고 제네시스를 재설정한다
bc.currentBlock.Store(bc.genesisBlock)
}
}
// Rewind the fast block in a simpleton way to the target head
// 단순하게 Fast block을 타겟헤드로 되돌린다
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && currentHeader.Number.Uint64() < currentFastBlock.NumberU64() {
bc.currentFastBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
}
// If either blocks reached nil, reset to the genesis state
// 블록이 nil에 도달하면 genesis상태를 재설정한다
if currentBlock := bc.CurrentBlock(); currentBlock == nil {
bc.currentBlock.Store(bc.genesisBlock)
}
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock == nil {
bc.currentFastBlock.Store(bc.genesisBlock)
}
currentBlock := bc.CurrentBlock()
currentFastBlock := bc.CurrentFastBlock()
rawdb.WriteHeadBlockHash(bc.db, currentBlock.Hash())
rawdb.WriteHeadFastBlockHash(bc.db, currentFastBlock.Hash())
return bc.loadLastState()
}
// FastSyncCommitHead sets the current head block to the one defined by the hash
// irrelevant what the chain contents were prior.
// 현재 해드블록을 해시와 무관한 체인 컨텐츠를 중시하는 블록으로 설정한다.
func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
// Make sure that both the block as well at its state trie exists
// 블록뿐만아니라, 상태 트라이도 존재하는지를 확인한다
block := bc.GetBlockByHash(hash)
if block == nil {
return fmt.Errorf("non existent block [%x…]", hash[:4])
}
if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB(), 0); err != nil {
return err
}
// If all checks out, manually set the head block
// 모든 체크가 끝나면 헤드블록을 수동으로 설정한다
bc.mu.Lock()
bc.currentBlock.Store(block)
bc.mu.Unlock()
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
return nil
}
// GasLimit returns the gas limit of the current HEAD block.
// GasLimit함수는 현재 헤드블록의 가스 한도를 반환한다
func (bc *BlockChain) GasLimit() uint64 {
return bc.CurrentBlock().GasLimit()
}
// CurrentBlock retrieves the current head block of the canonical chain. The
// block is retrieved from the blockchain's internal cache.
// 이 함수는 캐노니컬 체인의 해드블록을 반환한다.
// 이 불록은 블록체인의 내부 캐시로부터 반환된다
func (bc *BlockChain) CurrentBlock() *types.Block {
return bc.currentBlock.Load().(*types.Block)
}
// CurrentFastBlock retrieves the current fast-sync head block of the canonical
// chain. The block is retrieved from the blockchain's internal cache.
// 이 함수는 캐노니컬 체인의 패스트 싱크 헤드 블록을 반환한다.
// 이 불록은 블록체인의 내부 캐시로부터 반환된다
func (bc *BlockChain) CurrentFastBlock() *types.Block {
return bc.currentFastBlock.Load().(*types.Block)
}
// SetProcessor sets the processor required for making state modifications.
// 스테이트 변경을 위해 필요한 프로세서를 설정한다
func (bc *BlockChain) SetProcessor(processor Processor) {
bc.procmu.Lock()
defer bc.procmu.Unlock()
bc.processor = processor
}
// SetValidator sets the validator which is used to validate incoming blocks.
// 새로 들어온 블록의 검증을 위한 검증자를 설정한다
func (bc *BlockChain) SetValidator(validator Validator) {
bc.procmu.Lock()
defer bc.procmu.Unlock()
bc.validator = validator
}
// Validator returns the current validator.
// validator함수는 현재 검증자를 반환한다
func (bc *BlockChain) Validator() Validator {
bc.procmu.RLock()
defer bc.procmu.RUnlock()
return bc.validator
}
// Processor returns the current processor.
// Processor 함수는 현재 처리자를 반환한다
func (bc *BlockChain) Processor() Processor {
bc.procmu.RLock()
defer bc.procmu.RUnlock()
return bc.processor
}
// State returns a new mutable state based on the current HEAD block.
// 이 함수는 현재 헤드 블록을 기반으로 새로운 변환 가능한 스테이트를 반환한다
func (bc *BlockChain) State() (*state.StateDB, error) {
return bc.StateAt(bc.CurrentBlock().Root())
}
// StateAt returns a new mutable state based on a particular point in time.
// 이 함수는 특정시간의 한 지점을 기반으로 새로운 변환 가능한 스테이트를 반환한다
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
return state.New(root, bc.stateCache)
}
// Reset purges the entire blockchain, restoring it to its genesis state.
// 전체 블록체인을 깨끗이하고 태초 상태로 되돌린다
func (bc *BlockChain) Reset() error {
return bc.ResetWithGenesisBlock(bc.genesisBlock)
}
// ResetWithGenesisBlock purges the entire blockchain, restoring it to the
// specified genesis state.
// 전체 블록체인을 깨끗이하고 태초 상태로 되돌린다
func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
// Dump the entire block chain and purge the caches
if err := bc.SetHead(0); err != nil {
return err
}
bc.mu.Lock()
defer bc.mu.Unlock()
// Prepare the genesis block and reinitialise the chain
// 제네시스 블록을 준비하고, 체인을 재초기화 한다
if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
log.Crit("Failed to write genesis block TD", "err", err)
}
rawdb.WriteBlock(bc.db, genesis)
bc.genesisBlock = genesis
bc.insert(bc.genesisBlock)
bc.currentBlock.Store(bc.genesisBlock)
bc.hc.SetGenesis(bc.genesisBlock.Header())
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
bc.currentFastBlock.Store(bc.genesisBlock)
return nil
}
// repair tries to repair the current blockchain by rolling back the current block
// until one with associated state is found. This is needed to fix incomplete db
// writes caused either by crashes/power outages, or simply non-committed tries.
//
// This method only rolls back the current block. The current header and current
// fast block are left intact.
// 이 함수는 특정 스테이트가 찾아질때까지 커런트 블록을 롤백하면서
// 현재 블록 체인을 수정한다. 이 함수는 아직 커밋되지 않은 트라이
// 크래시를 유발하는 미완성의 DB 쓰기 동작을 수정해야 한다
func (bc *BlockChain) repair(head **types.Block) error {
for {
// Abort if we've rewound to a head block that does have associated state
// 만약 상태가 없는 헤드블록을 되돌리려 했다면 abort
if _, err := state.New((*head).Root(), bc.stateCache); err == nil {
log.Info("Rewound blockchain to past state", "number", (*head).Number(), "hash", (*head).Hash())
return nil
}
// Otherwise rewind one block and recheck state availability there
// 아니라면 블록을 하나 되돌리고 상태가 가능한지 다시 체크한다
(*head) = bc.GetBlock((*head).ParentHash(), (*head).NumberU64()-1)
}
}
// Export writes the active chain to the given writer.
// Export함수는 활성체인을 주어진 쓰기기능에 쓴다
func (bc *BlockChain) Export(w io.Writer) error {
return bc.ExportN(w, uint64(0), bc.CurrentBlock().NumberU64())
}
// ExportN writes a subset of the active chain to the given writer.
// Export함수는 활성체인의 일부를 주어진 쓰기기능에 쓴다
func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
bc.mu.RLock()
defer bc.mu.RUnlock()
if first > last {
return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
}
log.Info("Exporting batch of blocks", "count", last-first+1)
for nr := first; nr <= last; nr++ {
block := bc.GetBlockByNumber(nr)
if block == nil {
return fmt.Errorf("export failed on #%d: not found", nr)
}
if err := block.EncodeRLP(w); err != nil {
return err
}
}
return nil
}
// insert injects a new head block into the current block chain. This method
// assumes that the block is indeed a true head. It will also reset the head
// header and the head fast sync block to this very same block if they are older
// or if they are on a different side chain.
//
// Note, this function assumes that the `mu` mutex is held!
// 이 함수는 뉴 헤드 블록을 현재 블록체인에 주입한다.
// 이때 블록이 진짜 헤드일때를 가정한다.
// 이 함수는 헤드의 헤더와 헤드 패스트 싱크블록이 오래되었거나
// 다른 사이드 체인에 있을 경우 이 블록으로 초기화 할것이다.
func (bc *BlockChain) insert(block *types.Block) {
// If the block is on a side chain or an unknown one, force other heads onto it too
// 블록이 사이드체인에 있거나, 모르는 블록일 경우 다른 헤드를 강제한다
updateHeads := rawdb.ReadCanonicalHash(bc.db, block.NumberU64()) != block.Hash()
// Add the block to the canonical chain number scheme and mark as the head
// 블록을 캐노니컬 체인 숫자구조에 추가하고, 헤드로 설정한다
rawdb.WriteCanonicalHash(bc.db, block.Hash(), block.NumberU64())
rawdb.WriteHeadBlockHash(bc.db, block.Hash())
bc.currentBlock.Store(block)
// If the block is better than our head or is on a different chain, force update heads
// 블록이 우리의 헤더보다 좋거나, 다른 체인에 있다면, 헤드를 강제로 업데이트 한다
if updateHeads {
bc.hc.SetCurrentHeader(block.Header())
rawdb.WriteHeadFastBlockHash(bc.db, block.Hash())
bc.currentFastBlock.Store(block)
}
}
// Genesis retrieves the chain's genesis block.
// Genesis함수는 체인의 제네시스 블록을 반환한다
func (bc *BlockChain) Genesis() *types.Block {
return bc.genesisBlock
}
// GetBody retrieves a block body (transactions and uncles) from the database by
// hash, caching it if found.
// GetBody함수는 블록의 바디(트렌젝션과 엉클)를 해시를 이용하여 DB로부터 반환하고
// 찾아졌을 경우 캐싱한다
func (bc *BlockChain) GetBody(hash common.Hash) *types.Body {
// Short circuit if the body's already in the cache, retrieve otherwise
// Body가 이미 캐시에 존재힐경우 반환한다
if cached, ok := bc.bodyCache.Get(hash); ok {
body := cached.(*types.Body)
return body
}
number := bc.hc.GetBlockNumber(hash)
if number == nil {
return nil
}
body := rawdb.ReadBody(bc.db, hash, *number)
if body == nil {
return nil
}
// Cache the found body for next time and return
// 다음을 위해 찾아진 블록바디를 캐싱한다
bc.bodyCache.Add(hash, body)
return body
}
// GetBodyRLP retrieves a block body in RLP encoding from the database by hash,
// caching it if found.
// GetBodyRLP함수는 RLP인코딩된 블록의 바디를 해시를 이용하여 DB로 부터 반환하고 캐싱한다
func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {
// Short circuit if the body's already in the cache, retrieve otherwise
// Body가 이미 캐시에 존재힐경우 반환한다
if cached, ok := bc.bodyRLPCache.Get(hash); ok {
return cached.(rlp.RawValue)
}
number := bc.hc.GetBlockNumber(hash)
if number == nil {
return nil
}
body := rawdb.ReadBodyRLP(bc.db, hash, *number)
if len(body) == 0 {
return nil
}
// Cache the found body for next time and return
// 다음을 위해 찾아진 블록바디를 캐싱한다
bc.bodyRLPCache.Add(hash, body)
return body
}
// HasBlock checks if a block is fully present in the database or not.
// HasBlcok함수는 full 블록이 DB에 존재하는지 여부를 체크한다
func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool {
if bc.blockCache.Contains(hash) {
return true
}
return rawdb.HasBody(bc.db, hash, number)
}
// HasState checks if state trie is fully present in the database or not.
// HasState함수는 상태트라이가 모두 db에 존재하는 지를 체크한다
func (bc *BlockChain) HasState(hash common.Hash) bool {
_, err := bc.stateCache.OpenTrie(hash)
return err == nil
}
// HasBlockAndState checks if a block and associated state trie is fully present
// in the database or not, caching it if present.
// 이 함수는 블록과 관련된 스테이트 트라이가 DB에 모두 존재하는지 체크한다
func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool {
// Check first that the block itself is known
// 알려진 블록인지 먼저 확인한다
block := bc.GetBlock(hash, number)
if block == nil {
return false
}
return bc.HasState(block.Root())
}
// GetBlock retrieves a block from the database by hash and number,
// caching it if found.
// GetBlock함수는 해시와 번호를 이용해 블록을 반환하고 캐싱한다
func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
// Short circuit if the block's already in the cache, retrieve otherwise
if block, ok := bc.blockCache.Get(hash); ok {
return block.(*types.Block)
}
block := rawdb.ReadBlock(bc.db, hash, number)
if block == nil {
return nil
}
// Cache the found block for next time and return
// 다음을 위해 찾아진 블록을 캐싱한다
bc.blockCache.Add(block.Hash(), block)
return block
}
// GetBlockByHash retrieves a block from the database by hash, caching it if found.
// GetBlockByHash 함수는 해시를 이용해 DB의 block을 검색하고, 캐싱한다.
func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {
number := bc.hc.GetBlockNumber(hash)
if number == nil {
return nil
}
return bc.GetBlock(hash, *number)
}
// GetBlockByNumber retrieves a block from the database by number, caching it
// (associated with its hash) if found.
// GetBlockByHash 함수는 number를 이용해 DB의 block을 검색하고, 캐싱한다.
func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
hash := rawdb.ReadCanonicalHash(bc.db, number)
if hash == (common.Hash{}) {
return nil
}
return bc.GetBlock(hash, number)
}
// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
// GetReceiptsByHash함수는 주어진 블록의 모든 트렌젝션에 대한 영수증을 반환한다
func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
number := rawdb.ReadHeaderNumber(bc.db, hash)
if number == nil {
return nil
}
return rawdb.ReadReceipts(bc.db, hash, *number)
}
// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
// [deprecated by eth/62]
// GetBlocksFromHash함수는 해시와 관련된 블록부터 n-1조상까지 반환한다
// eth/62에서 제거됨
func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) {
number := bc.hc.GetBlockNumber(hash)
if number == nil {
return nil
}
for i := 0; i < n; i++ {
block := bc.GetBlock(hash, *number)
if block == nil {
break
}
blocks = append(blocks, block)
hash = block.ParentHash()
*number--
}
return
}
// GetUnclesInChain retrieves all the uncles from a given block backwards until
// a specific distance is reached.
// 이 블록의 뒷쪽 특정 거리까지 존재하는 엉클블록을 반환한다
func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {
uncles := []*types.Header{}
for i := 0; block != nil && i < length; i++ {
uncles = append(uncles, block.Uncles()...)
block = bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
}
return uncles
}
// TrieNode retrieves a blob of data associated with a trie node (or code hash)
// either from ephemeral in-memory cache, or from persistent storage.
// 이 함수는 트라이 노드나 코드해시에 관련된 데이터 블롭을 반환한다.
// 금방사라질 인메모리 캐시나 잔존하는 저장소에서
func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) {
return bc.stateCache.TrieDB().Node(hash)
}
// Stop stops the blockchain service. If any imports are currently in progress
// it will abort them using the procInterrupt.
// Stop함수는 블록체인 서비스를 중지한다. 만약 현재 처리중인 입수된 블록은
// proInterrupt를 이용해 abort한다
func (bc *BlockChain) Stop() {
if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) {
return
}
// Unsubscribe all subscriptions registered from blockchain
// 블록체인에 등록되었던 모든 구독을 해지한다
bc.scope.Close()
close(bc.quit)
atomic.StoreInt32(&bc.procInterrupt, 1)
bc.wg.Wait()
// Ensure the state of a recent block is also stored to disk before exiting.
// We're writing three different states to catch different restart scenarios:
// - HEAD: So we don't need to reprocess any blocks in the general case
// - HEAD-1: So we don't do large reorgs if our HEAD becomes an uncle
// - HEAD-127: So we have a hard limit on the number of blocks reexecuted
// 최근 블록의 상태를 보장한다
// 우리는 재시작 상태를 지원하기 위한 3가지의 상태를 저장할것이다
// HEAD: 아무 블록도 재검증할필요 없음
// HEAD-1: 우리의 헤더가 엉클이 되더라도 크게 재구성할필요는없다
// HEAD-127: 처리해야할 블록의 숫자에 강제제약이 있다
if !bc.cacheConfig.Disabled {
triedb := bc.stateCache.TrieDB()
for _, offset := range []uint64{0, 1, triesInMemory - 1} {
if number := bc.CurrentBlock().NumberU64(); number > offset {
recent := bc.GetBlockByNumber(number - offset)
log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root())
if err := triedb.Commit(recent.Root(), true); err != nil {
log.Error("Failed to commit recent state trie", "err", err)
}
}
}
for !bc.triegc.Empty() {
triedb.Dereference(bc.triegc.PopItem().(common.Hash), common.Hash{})
}
if size := triedb.Size(); size != 0 {
log.Error("Dangling trie nodes after full cleanup")
}
}
log.Info("Blockchain manager stopped")
}
// 퓨처블록을 처리한다
func (bc *BlockChain) procFutureBlocks() {
blocks := make([]*types.Block, 0, bc.futureBlocks.Len())
// 퓨처블록의 키들을 검색하여 키에 해당하는 해시가 있는 블록을 골라낸다.
for _, hash := range bc.futureBlocks.Keys() {
if block, exist := bc.futureBlocks.Peek(hash); exist {
blocks = append(blocks, block.(*types.Block))
}
}
if len(blocks) > 0 {
//블록넘버로 정렬해서 체인에 추가한다
types.BlockBy(types.Number).Sort(blocks)
// Insert one by one as chain insertion needs contiguous ancestry between blocks
// 이함수는 주어진 블록들을 캐노니컬 체인에 추가하거나, 포크를 생성한다
// 추가가 끝나면, 누적되었던 모든 이벤트가 발생함
for i := range blocks {
bc.InsertChain(blocks[i : i+1])
}
}
}
// WriteStatus status of write
// WriteStatus는 쓰기기능의 상태이다
type WriteStatus byte
const (
NonStatTy WriteStatus = iota
CanonStatTy
SideStatTy
)
// Rollback is designed to remove a chain of links from the database that aren't
// certain enough to be valid.
// Rollback함수는 신뢰할만하지 않은 체인의 연결을 DB로 부터 제거하기 위해 디자인되었다
func (bc *BlockChain) Rollback(chain []common.Hash) {
bc.mu.Lock()
defer bc.mu.Unlock()
for i := len(chain) - 1; i >= 0; i-- {
hash := chain[i]
currentHeader := bc.hc.CurrentHeader()
if currentHeader.Hash() == hash {
bc.hc.SetCurrentHeader(bc.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1))
}
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock.Hash() == hash {
newFastBlock := bc.GetBlock(currentFastBlock.ParentHash(), currentFastBlock.NumberU64()-1)
bc.currentFastBlock.Store(newFastBlock)
rawdb.WriteHeadFastBlockHash(bc.db, newFastBlock.Hash())
}
if currentBlock := bc.CurrentBlock(); currentBlock.Hash() == hash {
newBlock := bc.GetBlock(currentBlock.ParentHash(), currentBlock.NumberU64()-1)
bc.currentBlock.Store(newBlock)
rawdb.WriteHeadBlockHash(bc.db, newBlock.Hash())
}
}
}
// SetReceiptsData computes all the non-consensus fields of the receipts
// SetReceiptsData함수는 영수증상의 합의가 아닌 모든 필드를 계산한다
func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) error {
signer := types.MakeSigner(config, block.Number())
transactions, logIndex := block.Transactions(), uint(0)
if len(transactions) != len(receipts) {
return errors.New("transaction and receipt count mismatch")
}
for j := 0; j < len(receipts); j++ {
// The transaction hash can be retrieved from the transaction itself
// 트렌젝션 해시가 트렌젝션 자체로부터 반환가능할때
receipts[j].TxHash = transactions[j].Hash()
// The contract address can be derived from the transaction itself
// 계약 주소가 트렌젝션 자체로부터 유도가능할때
if transactions[j].To() == nil {
// Deriving the signer is expensive, only do if it's actually needed
// 서명자를 유도하는것은 비싸기때문에 실제로 필요할때만 진행
from, _ := types.Sender(signer, transactions[j])
receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce())
}
// The used gas can be calculated based on previous receipts
// 사용된 가스량이 이전 영수증으로부터 계산가능할때
if j == 0 {
receipts[j].GasUsed = receipts[j].CumulativeGasUsed
} else {
receipts[j].GasUsed = receipts[j].CumulativeGasUsed - receipts[j-1].CumulativeGasUsed
}
// The derived log fields can simply be set from the block and transaction
// 유도된 로그필드는 블록과 트렌젝션으로 부터 쉽게 채울수 있음
for k := 0; k < len(receipts[j].Logs); k++ {
receipts[j].Logs[k].BlockNumber = block.NumberU64()
receipts[j].Logs[k].BlockHash = block.Hash()
receipts[j].Logs[k].TxHash = receipts[j].TxHash
receipts[j].Logs[k].TxIndex = uint(j)
receipts[j].Logs[k].Index = logIndex
logIndex++
}
}
return nil
}
// InsertReceiptChain attempts to complete an already existing header chain with
// transaction and receipt data.
// 이 함수는 이미 존재하는 헤더체인을 트렌젝션과 영수증 데이터로 완성한다
func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
bc.wg.Add(1)
defer bc.wg.Done()
// Do a sanity check that the provided chain is actually ordered and linked
// 전달된 체인이 실제로 주문되었고, 연결되었는지 확인한다
for i := 1; i < len(blockChain); i++ {
if blockChain[i].NumberU64() != blockChain[i-1].NumberU64()+1 || blockChain[i].ParentHash() != blockChain[i-1].Hash() {
log.Error("Non contiguous receipt insert", "number", blockChain[i].Number(), "hash", blockChain[i].Hash(), "parent", blockChain[i].ParentHash(),
"prevnumber", blockChain[i-1].Number(), "prevhash", blockChain[i-1].Hash())
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, blockChain[i-1].NumberU64(),
blockChain[i-1].Hash().Bytes()[:4], i, blockChain[i].NumberU64(), blockChain[i].Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4])
}
}
var (
stats = struct{ processed, ignored int32 }{}
start = time.Now()
bytes = 0
batch = bc.db.NewBatch()
)
for i, block := range blockChain {
receipts := receiptChain[i]
// Short circuit insertion if shutting down or processing failed
// 종료시나 처리가 실패했을 경우
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
return 0, nil
}
// Short circuit if the owner header is unknown
// 헤더가 알려지지 않았을 경우
if !bc.HasHeader(block.Hash(), block.NumberU64()) {
return i, fmt.Errorf("containing header #%d [%x…] unknown", block.Number(), block.Hash().Bytes()[:4])
}
// Skip if the entire data is already known
// 모든데이터가 알려진 경우 넘어감
if bc.HasBlock(block.Hash(), block.NumberU64()) {
stats.ignored++
continue
}
// Compute all the non-consensus fields of the receipts
// 영수증상의 합의가 아닌 모든 부분을 계산
if err := SetReceiptsData(bc.chainConfig, block, receipts); err != nil {
return i, fmt.Errorf("failed to set receipts data: %v", err)
}
// Write all the data out into the database
// 모든데이터를 DB에 쓴다
rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts)
rawdb.WriteTxLookupEntries(batch, block)
stats.processed++
if batch.ValueSize() >= ethdb.IdealBatchSize {
if err := batch.Write(); err != nil {
return 0, err
}
bytes += batch.ValueSize()
batch.Reset()
}
}
if batch.ValueSize() > 0 {
bytes += batch.ValueSize()
if err := batch.Write(); err != nil {
return 0, err
}
}
// Update the head fast sync block if better
// fast sync 헤드블록이 더 좋을 경우 업데이트 한다
bc.mu.Lock()
head := blockChain[len(blockChain)-1]
if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
currentFastBlock := bc.CurrentFastBlock()