本文探究 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目录下。密钥文件最好经常换密码,并以多种形式存放最为妥当。
 
      