以太坊之命令列建立賬戶
程式碼: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) //重新命名
}
總結
到目前為止,我們使用命令列建立賬戶得過程到此為止,本文只是大概梳理了下建立過程,並未探究私鑰得具體過程,後面有機會會專門寫一下這個過程。