GO-HPB源码解读--节点初始化

HPB在创建帐号时使用的命令是 ./ghpb --datadir node/data init gensis.json
通过代码看下节点初始化做了什么事情

init命令定义在chaincmd.go文件中,关于urfave和utils.MigrateFlags在创建帐号中做过说明,这里直接看下initGenesis做了什么事情。

initGenesis方法流程:

  1. MakeConfigNode方法配置节点的默认参数信息,这个时候如果很多参数没有通过命令很输入进来,程序会把所有的参数信息设置为默认值
  2. 加载文件gensis.json文件,文件名通过init的flag参数输入进行。后边我们对gensis.json文件解读一下。
  3. 创建bc.Genesis对象,然后通过gensis.json文件的信息进行赋值
  4. db.OpenDatabase创建数据库
  5. bc.SetupGenesisBlockf进行初始块的生成和写入文件

github.com\hpb-project\go-hpb\cmd\chaincmd.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
	initCommand = cli.Command{
Action: utils.MigrateFlags(initGenesis),
Name: "init",
Usage: "Bootstrap and initialize a new genesis block",
ArgsUsage: "<genesisPath>",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.LightModeFlag,
},
Category: "BLOCKCHAIN COMMANDS",
Description: ``,
}

// initGenesis will initialise the given JSON format genesis file and writes it as
// the zero'd block (i.e. genesis) or will fail hard if it can't succeed.
func initGenesis(ctx *cli.Context) error {
// Open an initialise both full and light databases
//stack, _ := MakeConfigNode(ctx)
MakeConfigNode(ctx)
// Make sure we have a valid genesis JSON
genesisPath := ctx.Args().First()
if len(genesisPath) == 0 {
utils.Fatalf("Must supply path to genesis JSON file")
}
file, err := os.Open(genesisPath)
if err != nil {
log.Warn("genesis path is %s", genesisPath)
utils.Fatalf("Failed to read genesis file: %v", err)
}
defer file.Close()

genesis := new(bc.Genesis)
if err := json.NewDecoder(file).Decode(genesis); err != nil {
utils.Fatalf("invalid genesis file: %v", err)
}

for _, name := range []string{"chaindata"} {
chaindb, err := db.OpenDatabase(name, 0, 0)
if err != nil {
utils.Fatalf("Failed to open database: %v", err)
}

_, hash, err := bc.SetupGenesisBlock(chaindb, genesis)

if err != nil {
utils.Fatalf("Failed to write genesis block: %v", err)
}
log.Info("Successfully wrote genesis state", "database", name, "hash", hash)
}
return nil
}

看下创建数据库的代码,数据库实例通过atomic.Value{}来实现线程安全操作。当没有传入–datadir参数时,会直接返回内存数据库,否则会创建chaindata数据库文件。ResolvePath方法会在chaindata路径前加个ghpb路径。数据库使用的是Google的levelDB,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// OpenDatabase opens an existing database with the given name (or creates one
// if no previous can be found) from within the node's data directory. If the
// node is an ephemeral one, a memory database is returned.
func OpenDatabase(name string, cache int, handles int) (hpbdb.Database, error) {

if DBINSTANCE.Load() != nil {
return DBINSTANCE.Load().(*hpbdb.LDBDatabase),nil
}

var cfg = config.GetHpbConfigInstance()
if cfg.Node.DataDir == ""{
return hpbdb.NewMemDatabase()
}
db, err := hpbdb.NewLDBDatabase(cfg.Node.ResolvePath(name), cache, handles)
if err != nil {
return nil, err
}
DBINSTANCE.Store(db)

return db, nil
}

SetupGenesisBlock方法主要是对初始区块的文件写入。

  1. GetCanonicalHash(db, 0)首先从数据库获取第0个hash,如果没有则返回一个初始hash,即[32]byte{0}
  2. 把初始的genesis信息生成初始区块提交到数据库
  3. …后续再读
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
func SetupGenesisBlock(db hpbdb.Database, genesis *Genesis) (*config.ChainConfig, common.Hash, error) {
if genesis != nil && genesis.Config == nil {
return config.MainnetChainConfig, common.Hash{}, errGenesisNoConfig
}

// Just commit the new block if there is no stored genesis block.
stored := GetCanonicalHash(db, 0)

if (stored == common.Hash{}) {

if genesis == nil {
genesis = DefaultGenesisBlock()
} else {
log.Info("Writing custom genesis block")
}
block, err := genesis.Commit(db)
return genesis.Config, block.Hash(), err
}

// Check whether the genesis block is already written.
if genesis != nil {
block, _ := genesis.ToBlock()
hash := block.Hash()
if hash != stored {
return genesis.Config, block.Hash(), &GenesisMismatchError{stored, hash}
}
}

// Get the existing chain configuration.
newcfg := genesis.configOrDefault(stored)
storedcfg, err := GetChainConfig(db, stored)
if err != nil {
if err == ErrChainConfigNotFound {
// This case happens if a genesis write was interrupted.
log.Warn("Found genesis block without chain config")
err = WriteChainConfig(db, stored, newcfg)
}
return newcfg, stored, err
}
// Special case: don't change the existing config of a non-mainnet chain if no new
// config is supplied. These chains would get AllProtocolChanges (and a compat error)
// if we just continued here.
if genesis == nil && stored != config.MainnetGenesisHash {
return storedcfg, stored, nil
}

// Check config compatibility and write the config. Compatibility errors
// are returned to the caller unless we're already at block zero.
height := GetBlockNumber(db, GetHeadHeaderHash(db))
if height == missingNumber {
return newcfg, stored, fmt.Errorf("missing block number for head header hash")
}
compatErr := storedcfg.CheckCompatible(newcfg, height)
if compatErr != nil && height != 0 && compatErr.RewindTo != 0 {
return newcfg, stored, compatErr
}
return newcfg, stored, WriteChainConfig(db, stored, newcfg)
}

genesis.Commit(db)方法中,首先将genesis转化成一个区块,然后再将区块信息写入数据库,同时包含其他一些数据内容,比如blockReceipts、GetCanonicalHash,WriteHeadBlockHash等等。此时已接触到初始block,后续文章我们对相关数据结构进行详细说明

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
// Commit writes the block and state of a genesis specification to the database.
// The block is committed as the canonical head block.
func (g *Genesis) Commit(db hpbdb.Database) (*types.Block, error) {
block, statedb := g.ToBlock()
if block.Number().Sign() != 0 {
return nil, fmt.Errorf("can't commit genesis block with number > 0")
}
if _, err := statedb.CommitTo(db, false); err != nil {
return nil, fmt.Errorf("cannot write state: %v", err)
}
if err := WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty); err != nil {
return nil, err
}
if err := WriteBlock(db, block); err != nil {
return nil, err
}
if err := WriteBlockReceipts(db, block.Hash(), block.NumberU64(), nil); err != nil {
return nil, err
}
if err := WriteCanonicalHash(db, block.Hash(), block.NumberU64()); err != nil {
return nil, err
}
if err := WriteHeadBlockHash(db, block.Hash()); err != nil {
return nil, err
}
if err := WriteHeadHeaderHash(db, block.Hash()); err != nil {
return nil, err
}
configtemp := g.Config
if configtemp == nil {
configtemp = config.MainnetChainConfig
}

return block, WriteChainConfig(db, block.Hash(), configtemp)
}