1. 程式人生 > >geth原始碼閱讀——建立外部賬號的流程

geth原始碼閱讀——建立外部賬號的流程

本文探究 geth account new 命令的程式碼執行流程

一、尋找命令入口

1.1. go-ethereum工程/cmd/geth/main.go

func init() {

   // Initialize the CLI app and start Geth

   app.Action = geth

   app.HideVersion = true // we have a commandto print the version

   app.Copyright = "Copyright 2013-2018The go-ethereum Authors"

   app.Commands = []cli.Command{

      ........................

      // See accountcmd.go:

      accountCommand,            //從這裡知道,與account命令相關的程式碼在accountCommand.go檔案中

}

1.2. 開啟 cms/geth/accountcmd.go

Subcommands:[]cli.Command{

...................

{

   Name:  "new",

   Usage: "Create a new account",

   Action: utils.MigrateFlags(accountCreate),   

 //從這裡可以知道 new 命令的執行函式在accountCreate函式中

   Flags: []cli.Flag{

      utils.DataDirFlag,

      utils.KeyStoreDirFlag,

      utils.PasswordFileFlag,

      utils.LightKDFFlag,

   },

................

二、accountCreate函式執行流程

// accountcmd.go

funcaccountCreate(ctx *cli.Context) error {

   …… 載入配置檔案, 獲取keydir,scryptN, scryptP

   //提示輸入密碼

   password := getPassPhrase("Your newaccount is locked with a password. Please give a password. Do not forget thispassword.", true, 0, utils.MakePasswordList(ctx))

//這裡建立地址,生成公鑰和私鑰

   address, err := keystore.StoreKey(keydir,password, scryptN, scryptP)

   //列印地址

   fmt.Printf("Address: {%x}\n",address)

   return nil

}

/////////////////////////////////////////////////////////////////////////

//accounts/keystore/keystore_passphrase.go

//具體表現為生成一對公私鑰,再由私鑰算出地址並構建一個自定義的Key

// StoreKey generatesa key, encrypts with 'auth' and stores in the given directory
func StoreKey(dir, auth string, scryptN, scryptPint) (common.Address, error) {
   _, a, err :=
storeNewKey(&keyStorePassphrase{dir,scryptN, scryptP}, crand.Reader, auth)
  
return a.Address,err
}

/////////////////////////////////////////////////////////////////////////////

//accounts/keystore/key.go

func storeNewKey(ks keyStore, rand io.Reader, authstring) (*Key, accounts.Account, error) {
   key, err :=
newKey(rand)    //生成金鑰對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))}}

//私鑰被儲存到磁碟,儲存前使用密碼加密,金鑰檔案儲存在你的以太坊客戶端keystore 子目錄中if err :=ks.StoreKey(a.URL.Path, key, auth); err != nil {   
      zeroKey(key.PrivateKey)
     
return nil,a, err
   }
  
return key,a, err
}

----------------------------------------------

func newKey(rand io.Reader) (*Key, error) {

// ecdsa橢圓曲線數字簽名演算法,這裡使用ecdsa生成一對公私鑰,並選擇的是secp256k1曲線。
   privateKeyECDSA, err :=ecdsa.GenerateKey(crypto.S256(), rand)           
  
if err !=nil {
     
return nil,err
   }
  
return newKeyFromECDSA(privateKeyECDSA),nil
}

---------------------------------------------------

// GenerateKeygenerates a public and private key pair.
func GenerateKey(c elliptic.Curve, rand io.Reader)(*PrivateKey, error) {
   k, err := randFieldElement(c, rand)
  
if err !=nil {
     
return nil,err
   }
   priv := new(PrivateKey)                               
//生成私鑰
   priv.PublicKey.Curve = c
   priv.D = k
   priv.PublicKey.X, priv.PublicKey.Y =c.ScalarBaseMult(k.Bytes())        
//生成公鑰return priv,nil
}

---------------------------------------------------------------------------------------

func newKeyFromECDSA(privateKeyECDSA*ecdsa.PrivateKey) *Key {
   id := uuid.NewRandom()
   key := &Key{
      Id:         id,
      Address:   crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),                      
//地址是公鑰轉過來的,僅20位元組
      PrivateKey: privateKeyECDSA,
   }
  
return key
}

-------------------------------------------------------------------------------------

func PubkeyToAddress(p ecdsa.PublicKey)common.Address {
   pubBytes := FromECDSAPub(&p)
  
return common.BytesToAddress(Keccak256(pubBytes[1:])[12:])                                            //公鑰經過Keccak-256單向雜湊函式變成了256bit                                                                                                    
}

-------------------------------------------------------------------------------------

func BytesToAddress(b []byte) Address {
  
var aAddress
   a.SetBytes(b)
  
return a
}

--------------------------------------------------------------------------------------------

func (a *Address) SetBytes(b []byte) {
  
if len(b)> len(a) {
      b = b[len(b)-
AddressLength:]                                                     //               AddressLength等於20                                                                                                                                                
   }
   copy(a[
AddressLength-len(b):], b)                                                    //取後20位元組(160bit, 即40個16進位制字元)作為地址
}

三、總結

每個賬戶都由一對鑰匙定義,一個私鑰(PrivateKey)和一個公鑰(Public Key)。 賬戶以地址為索引,地址由公鑰衍生而來,取公鑰的最後20個位元組。每對私鑰/地址都編碼在一個鑰匙檔案裡(Keystore)。

地址的生成的流程是:私鑰 -> 公鑰 -> 地址。因此地址的生成需要三步:

1、 由secp256k1曲線生成私鑰,是由隨機的256bit組成(32位元組)

2、採用橢圓曲線數字簽名演算法(ECDSA)將私鑰對映成公鑰(64位元組)

3、公鑰經過Keccak-256單向雜湊函式變成了256bit,然後取160bit作為地址(20位元組)

注意:私鑰極其重要,使用者輸入的密碼用來對私鑰加密,加密後的金鑰檔案被儲存到keystore目錄下。金鑰檔案最好經常換密碼,並以多種形式存放最為妥當。