GO-HPB源码解读--多帐号解锁

在启动节点时,解锁参数可以同时指定多个帐号,格式为
--unlock "addr1,addr2,addr3..." --password "pwd",其中unlock参数为多个帐号的地址,用逗号分隔;password参数为口令文件,文件中的口令每行一个口令。
在创建帐号时,命令./ghpb --datadir node/data account init可以多次执行,每次执行后,会在路径node/data/keystore下生成多个帐号文件,比如UTC–2018-11-29T08-21-25.565157387Z–3e8aadb68222c70b309e87cd5c27e97950879076。当节点启动指定多个帐号时,会加载对应的帐号文件,并解锁。下边看一下解锁的代码。

启动节点入口main.go的init方法,调用过程为init()–>ghpb–>startNode()

1
2
3
4
5
6
7
func init() {
// Initialize the CLI app and start Geth
app.Action = ghpb
app.HideVersion = true // we have a command to print the version
app.Copyright = "Copyright 2013-2018 The go-hpb Authors "
app.Commands = []cli.Command{
...省略...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ghpb is the main entry point into the system if no special subcommand is ran.
// It creates a default node based on the command line arguments and runs it in
// blocking mode, waiting for it to be shut down.
func ghpb(ctx *cli.Context) error {
cfg := MakeConfigNode(ctx)
hpbnode, err := createNode(cfg)
if err != nil {
utils.Fatalf("Failed to create node")
return err
}
startNode(ctx, hpbnode, cfg)
hpbnode.Wait()
return nil
}

在startNode方法中

  1. 首先获取keyStore的指针
  2. MakePasswordList方法解析–password参数指定的口令文件,文件中的内容每一行对应一个口令
  3. 获取帐号列表,进行遍历解锁
  4. 后边是启动节点,并启动一个协程,开启钱包事件监听。这部分内容后续章节中再进行解读。下边看下遍历时,每个帐号是怎么使用口令解锁的。
    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
    // startNode boots up the system node and all registered protocols, after which
    // it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
    // miner.
    func startNode(ctx *cli.Context, stack *node.Node, conf *config.HpbConfig) {

    // Unlock any account specifically requested
    ks := stack.AccountManager().KeyStore().(*keystore.KeyStore)

    passwords := utils.MakePasswordList(ctx)
    unlocks := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
    for i, account := range unlocks {
    if trimmed := strings.TrimSpace(account); trimmed != "" {
    unlockAccount(ctx, ks, trimmed, i, passwords)
    }
    }

    if unlocks[0] != "" {
    account, err := utils.MakeAddress(ks, strings.TrimSpace(unlocks[0]))
    if err != nil {
    utils.Fatalf("Could not list accounts: %v", err)
    }
    conf.Node.DefaultAddress = account.Address
    }

    //set rpc aii
    //utils.SetNodeAPI(&conf.Node, stack)
    // Start up the node itself
    utils.StartNode(stack)
    // Register wallet event handlers to open and auto-derive wallets
    events := make(chan accounts.WalletEvent, 16)
    stack.AccountManager().Subscribe(events)

    go func() {

    // Open any wallets already attached
    for _, wallet := range stack.AccountManager().Wallets() {
    if err := wallet.Open(""); err != nil {
    log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err)
    }
    }
    // Listen for wallet event till termination
    for event := range events {
    switch event.Kind {
    case accounts.WalletArrived:
    if err := event.Wallet.Open(""); err != nil {
    log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err)
    }
    case accounts.WalletOpened:
    status, _ := event.Wallet.Status()
    log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status)

    case accounts.WalletDropped:
    log.Info("Old wallet dropped", "url", event.Wallet.URL())
    event.Wallet.Close()
    }
    }
    }()

    // Start auxiliary services if enabled
    if ctx.GlobalBool(utils.MiningEnabledFlag.Name) && (conf.Network.RoleType == "") {
    // Set the gas price to the limits from the CLI and start mining
    stack.TxPool().SetGasPrice(utils.GlobalBig(ctx, utils.GasPriceFlag.Name))
    if err := stack.StartMining(true); err != nil {
    utils.Fatalf("Failed to start mining: %v", err)
    }
    }
    }

在unlockAccount方法中,每个帐号会进行尝试3次解锁。每次会从口令列表中取出一个口令进行解锁帐号,如果解锁成功则返回,失败则继续。方法getPassPhrase每次从众多口令中取一个口令的方式是以下部分代码实现的 ,如果口令个数大于3个的话,则每次都会取最后一个。不是很理解为何要这样做,按理说应该每个帐号对应一个口令的。😂😂😂

1
2
3
4
5
6
if len(passwords) > 0 {
if i < len(passwords) {
return passwords[i]
}
return passwords[len(passwords)-1]
}