從命令列開始解析同步區塊的程式碼
從命令列開始解析同步區塊的程式碼
一.同步簡介
我們都知道geth支援三種同步模式
fast模式:從開始到結束,獲取區塊的header,獲取區塊的body,從創始塊開始校驗每一個元素,需要下載所有區塊資料資訊。速度最慢,但是能獲取到所有的歷史資料。
full模式:獲取區塊的header,獲取區塊的body,在同步到當前塊之前不處理任何事務。然後獲得一個快照,此後,像full節點一樣進行後面的同步操作。這種方法用得最多,目的在不要在意歷史資料,將歷史資料按照快照的方式,不逐一驗證,沿著區塊下載最近資料庫中的交易,有可能丟失歷史資料。此方法可能會對歷史資料有部分丟失,但是不影響今後的使用。
light模式:僅獲取當前狀態。驗證元素需要向full節點發起相應的請求。
二.從命令列開始解析同步區塊程式碼
geth的main包配置函式utils.SyncModeFlag,在util包中可以看到Name和Usage引數
nodeFlags = []cli.Flag{ utils.IdentityFlag, utils.UnlockedAccountFlag, utils.PasswordFileFlag, utils.BootnodesFlag, utils.BootnodesV4Flag, utils.BootnodesV5Flag, utils.DataDirFlag, utils.KeyStoreDirFlag, utils.NoUSBFlag, utils.DashboardEnabledFlag, utils.DashboardAddrFlag, utils.DashboardPortFlag, utils.DashboardRefreshFlag, utils.EthashCacheDirFlag, utils.EthashCachesInMemoryFlag, utils.EthashCachesOnDiskFlag, utils.EthashDatasetDirFlag, utils.EthashDatasetsInMemoryFlag, utils.EthashDatasetsOnDiskFlag, utils.TxPoolNoLocalsFlag, utils.TxPoolJournalFlag, utils.TxPoolRejournalFlag, utils.TxPoolPriceLimitFlag, utils.TxPoolPriceBumpFlag, utils.TxPoolAccountSlotsFlag, utils.TxPoolGlobalSlotsFlag, utils.TxPoolAccountQueueFlag, utils.TxPoolGlobalQueueFlag, utils.TxPoolLifetimeFlag, utils.FastSyncFlag, utils.LightModeFlag, utils.SyncModeFlag, utils.GCModeFlag, utils.LightServFlag, utils.LightPeersFlag, utils.LightKDFFlag, utils.CacheFlag, utils.CacheDatabaseFlag, utils.CacheGCFlag, utils.TrieCacheGenFlag, utils.ListenPortFlag, utils.MaxPeersFlag, utils.MaxPendingPeersFlag, utils.EtherbaseFlag, utils.GasPriceFlag, utils.MinerThreadsFlag, utils.MiningEnabledFlag, utils.TargetGasLimitFlag, utils.NATFlag, utils.NoDiscoverFlag, utils.DiscoveryV5Flag, utils.NetrestrictFlag, utils.NodeKeyFileFlag, utils.NodeKeyHexFlag, utils.DeveloperFlag, utils.DeveloperPeriodFlag, utils.TestnetFlag, utils.RinkebyFlag, utils.VMEnableDebugFlag, utils.NetworkIdFlag, utils.RPCCORSDomainFlag, utils.RPCVirtualHostsFlag, utils.EthStatsURLFlag, utils.MetricsEnabledFlag, utils.FakePoWFlag, utils.NoCompactionFlag, utils.GpoBlocksFlag, utils.GpoPercentileFlag, utils.ExtraDataFlag, configFileFlag, }
flag配置詳情
defaultSyncMode = eth.DefaultConfig.SyncMode
SyncModeFlag = TextMarshalerFlag{
Name: "syncmode",
Usage: `Blockchain sync mode ("fast", "full", or "light")`,
Value: &defaultSyncMode,
}
flag相關配置的結構體型別
type TextMarshalerFlag struct { Name string Value TextMarshaler Usage string }
TextMarshalerFlag操作的成員方法
func (f TextMarshalerFlag) GetName() string {
return f.Name
}
func (f TextMarshalerFlag) String() string {
return fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)
}
func (f TextMarshalerFlag) Apply(set *flag.FlagSet) {
eachName(f.Name, func(name string) {
set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage)
})
}
從全域性的flag配置中返回一個TextMarshalerFlag配置標誌
func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler {
val := ctx.GlobalGeneric(name)
if val == nil {
return nil
}
return val.(textMarshalerVal).v
}
預設配置使用主網路的程式碼
var DefaultConfig = Config{
SyncMode: downloader.FastSync,
Ethash: ethash.Config{
CacheDir: "ethash",
CachesInMem: 2,
CachesOnDisk: 3,
DatasetsInMem: 1,
DatasetsOnDisk: 2,
},
NetworkId: 1,
LightPeers: 100,
DatabaseCache: 768,
TrieCache: 256,
TrieTimeout: 5 * time.Minute,
GasPrice: big.NewInt(18 * params.Shannon),
TxPool: core.DefaultTxPoolConfig,
GPO: gasprice.Config{
Blocks: 20,
Percentile: 60,
},
}
下載模式配置的程式碼,本段程式碼存在於eth包中的config.go的config結構體內部
SyncMode downloader.SyncMode
同步的型別是SyncMode,而SyncMode的真實型別是int。const常量的定義給不同模式分別賦值:
- full:0
- fast: 1
light: 2
type SyncMode int const ( FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks FastSync // Quickly download the headers, full sync only at the chain head LightSync // Download only the headers and terminate afterwards )
整個模式的變更程式碼請看downloader包中的modes.go
此方法比較簡單,當傳入的mode大於等於0並且小於等於2時返回true。可以簡單理解為是一個合法性的校驗
func (mode SyncMode) IsValid() bool {
return mode >= FullSync && mode <= LightSync
}
此段程式碼實現了stringer的介面,當被呼叫時會返回對應的字串描述:full,fast,light,unknown。此方法類似與Java中的toString方法。
func (mode SyncMode) String() string {
switch mode {
case FullSync:
return "full"
case FastSync:
return "fast"
case LightSync:
return "light"
default:
return "unknown"
}
}
此方法實現了encoding包下的TextMarshaler介面的MarshalText方法,根據傳入的同步型別值返回字串編碼(UTF-8-encoded)之後的文字內容。可以簡單理解為SyncMode(int)和文字內容的轉換。
func (mode SyncMode) MarshalText() ([]byte, error) {
switch mode {
case FullSync:
return []byte("full"), nil
case FastSync:
return []byte("fast"), nil
case LightSync:
return []byte("light"), nil
default:
return nil, fmt.Errorf("unknown sync mode %d", mode)
}
}
此方法實現了encoding包下的TextUnmarshaler介面的UnmarshalText方法,根據傳入的文字內容
返回SyncMode型別對應的值。可以簡單理解為文字內容和SyncMode(int)的轉換。
func (mode *SyncMode) UnmarshalText(text []byte) error {
switch string(text) {
case "full":
*mode = FullSync
case "fast":
*mode = FastSync
case "light":
*mode = LightSync
default:
return fmt.Errorf(`unknown sync mode %q, want "full", "fast" or "light"`, text)
}
return nil
}
同步模式中途的變更經過上面的程式碼分析我們是否就確定,如果不傳遞引數geth一直就是通過fast模式進行同步的麼?那麼,再看看下面的程式碼分析吧。
在eth/handler.go中方法NewProtocolManager中的程式碼:
// Figure out whether to allow fast sync or not
if mode == downloader.FastSync && blockchain.CurrentBlock().NumberU64() > 0 {
log.Warn("Blockchain not empty, fast sync disabled")
mode = downloader.FullSync
}
if mode == downloader.FastSync {
manager.fastSync = uint32(1)
}
這段程式碼是在建立ProtocolManager時進行同步模式的引數設定。blockchain.CurrentBlock()獲得當前的區塊資訊,NumberU64()返回的是最新區塊的頭部的number
func (b *Block) NumberU64() uint64 {
return b.header.Number.Uint64()
}
現在整理一下這段程式碼的整體邏輯就是,當同步模式為fast並最新區塊的高度大於0(已經同步過一部分資料)時,程式自動將同步模式轉變為full,並列印警告資訊。