1. 程式人生 > 其它 >以太坊之命令列建立賬戶

以太坊之命令列建立賬戶

技術標籤:以太坊gogolang區塊鏈

程式碼:github.com/ethereum/go-ethereum
版本:96157a897be2032be5fdb87f947fbe5df8a53bd4

本文講解一下用命令列建立賬戶得過程,命令列裡面建立得賬戶型別為KeyStore型別,正好印證一下前面寫的關於accounts包的文章。

命令列建立

使用命令geth account new,在Password:輸入密碼,並在Repeat password:確認密碼,最終會告訴我們地址是0xXXXXXXXXXXXXXXXXXXX,私鑰儲存在地址

地址是一個[20]byte得陣列

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-4nEzfIH7-1612234770866)(C:\Users\82628\AppData\Roaming\Typora\typora-user-images\image-20210202100221210.png)]

上圖中,我們輸入得密碼為123456,返回的地址為202f14c5005fe8737b03BFe88b9f3756bD4a08E6,十六進位制為0x202f14c5005fe8737b03BFe88b9f3756bD4a08E6,儲存地址為/root/.ethereum/keystore/UTC--2021-02-02T02-02-05.866484872Z--202f14c5005fe8737b03bfe88b9f3756bd4a08e6

建立過程

1、new命令

cmd/geth/accountcmd.go中找到了geth account new命令的入口:

{
            Name:   "new",
            Usage:
"Create a new account", Action: utils.MigrateFlags(accountCreate), Flags: []cli.Flag{ utils.DataDirFlag, utils.KeyStoreDirFlag, utils.PasswordFileFlag, utils.LightKDFFlag, }, Description:
` geth account new Creates a new account and prints the address. The account is saved in encrypted format, you are prompted for a password. You must remember this password to unlock your account in the future. For non-interactive use the password can be specified with the --password flag: Note, this is meant to be used for testing only, it is a bad idea to save your password to file or expose in any other way. `, },

我們可以看到上面action引數指向accountCreate函式:

// accountCreate creates a new account into the keystore defined by the CLI flags.
// accountCreate函式建立一個keystore型別賬戶
func accountCreate(ctx *cli.Context) error {
   cfg := gethConfig{Node: defaultNodeConfig()}
   // Load config file.
   if file := ctx.GlobalString(configFileFlag.Name); file != "" {
      if err := loadConfig(file, &cfg); err != nil {
         utils.Fatalf("%v", err)
      }
   }
   utils.SetNodeConfig(ctx, &cfg.Node) //初始化設定
   scryptN, scryptP, keydir, err := cfg.Node.AccountConfig() //加密演算法引數及儲存地址

   if err != nil {
      utils.Fatalf("Failed to read configuration: %v", err)
   }

   password := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) //如果有指定密碼,則返回密碼列表第一個;否則要求輸入密碼以及確認密碼。呼叫github.com/ethereum/go-ethereum/console/prompt包實現。

   account, err := keystore.StoreKey(keydir, password, scryptN, scryptP) //建立賬戶

   if err != nil {
      utils.Fatalf("Failed to create account: %v", err)
   }
   fmt.Printf("\nYour new key was generated\n\n")
   fmt.Printf("Public address of the key:   %s\n", account.Address.Hex())
   fmt.Printf("Path of the secret key file: %s\n\n", account.URL.Path)
   fmt.Printf("- You can share your public address with anyone. Others need it to interact with you.\n")
   fmt.Printf("- You must NEVER share the secret key with anyone! The key controls access to your funds!\n")
   fmt.Printf("- You must BACKUP your key file! Without the key, it's impossible to access account funds!\n")
   fmt.Printf("- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!\n\n")
   return nil
}

我們可以看到前面很多都是在做讀取配置等操作,只有在最後才呼叫了keystore.StoreKey函式來進行建立賬戶操作。

2、keystore.StoreKey函式

accountCreate中,我們呼叫了accounts/keystore/passphrase.go中的StoreKey函式來進行建立賬戶。

// StoreKey generates a key, encrypts with 'auth' and stores in the given directory
func StoreKey(dir, auth string, scryptN, scryptP int) (accounts.Account, error) {
   _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth)
   return a, err
}

我們可以看到直接構造了keyStorePassphrase結構體作為storeNewKey函式得引數。

func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) {
   key, err := newKey(rand) //利用rand產生隨機數,並用secp256k1演算法構造出Key物件,包含私鑰和D
   if err != nil {
      return nil, accounts.Account{}, err
   }
   a := accounts.Account{
      Address: key.Address,
      URL:     accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
   } //構造Account物件
   if err := ks.StoreKey(a.URL.Path, key, auth); err != nil { //加密私鑰
      zeroKey(key.PrivateKey) //不成功銷燬私鑰
      return nil, a, err
   }
   return key, a, err
}

從上面可以看到,最終呼叫了ks.StoreKey加密私鑰

func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
   keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)  //基於key,scryptN,scryptP,auth(密碼)生成加密key物件
   if err != nil {
      return err
   }
   // Write into temporary file
   tmpName, err := writeTemporaryKeyFile(filename, keyjson) //將加密之後的私鑰寫到檔案裡面
   if err != nil {
      return err
   }
   if !ks.skipKeyFileVerification {
      // Verify that we can decrypt the file with the given password.
      _, err = ks.GetKey(key.Address, tmpName, auth)  //驗證私鑰
      if err != nil {
         msg := "An error was encountered when saving and verifying the keystore file. \n" +
            "This indicates that the keystore is corrupted. \n" +
            "The corrupted file is stored at \n%v\n" +
            "Please file a ticket at:\n\n" +
            "https://github.com/ethereum/go-ethereum/issues." +
            "The error was : %s"
         //lint:ignore ST1005 This is a message for the user
         return fmt.Errorf(msg, tmpName, err)
      }
   }
   return os.Rename(tmpName, filename) //重新命名
}

總結

到目前為止,我們使用命令列建立賬戶得過程到此為止,本文只是大概梳理了下建立過程,並未探究私鑰得具體過程,後面有機會會專門寫一下這個過程。