Accounts源碼分析與邏輯結構1

Accounts源碼分析與邏輯結構1

總所周知以太坊在比特幣的基礎上加以引用與改進,比特幣使用UTXO來表示狀態的轉移,而以太坊使用帳來表示狀態的轉移。算法

accounts包實現了以太坊客戶端的錢包和帳戶管理

在以太坊網絡中存在兩種帳戶:

外部帳戶EOA:通常是屬於我的或者用戶的帳戶,被私鑰控制沒有任何代碼與之相關後端

內部帳戶CA:給智能合約分配的帳戶,被合約代碼控制,且與合約關聯api

在源碼core/state/state_object.go文件下,帳戶定義以下:緩存

// Account is the Ethereum consensus representation of accounts.
// These objects are stored in the main account trie.
type Account struct {
   Nonce    uint64
   Balance  *big.Int
   Root     common.Hash // merkle root of the storage trie
   CodeHash []byte
}
複製代碼

Nonce:若是是EOA帳戶表示發送交易的序號,若是爲CA帳戶,則Nonce表示合約建立的序號網絡

Balance:表示帳戶的餘額,該帳戶地址對應的帳戶餘額數據結構

Root:存儲merkle樹的根,若是爲EOA帳戶root爲nildom

CodeHash:帳戶綁定的EVM Code,若是爲EOA則CodeHash爲nil異步

錢包interface,是指包含了一個或多個帳戶的軟件錢包或者硬件錢包:
type Wallet interface {
 // URL 用來獲取這個錢包能夠訪問的規範路徑。它會被上層使用用來從全部的後端的錢包來排序。
   URL() URL
    
 // 用來返回一個文本值用來標識當前錢包的狀態。同時也會返回一個error用來標識錢包遇到的任何錯誤。
   Status() (string, error)
    
  //Open初始化對錢包實例的訪問。若是你open了一個錢包,你必須close它。
   Open(passphrase string) error
    
 // Close 釋放由Open方法佔用的任何資源。 
   Close() error
    
  // Accounts用來獲取錢包發現了帳戶列表。對於分層次的錢包,這個列表不會詳盡的列出全部的帳號,而是隻包 //含在賬戶派生期間明確固定的賬戶。
   Accounts() []Account
    
  //包含返回賬戶是否屬於此特定錢包的一部分。
   Contains(account Account) bool
    
 //Derive嘗試在指定的派生路徑上顯式派生出分層肯定性賬戶。若是pin爲true,派生賬戶將被添加到錢包的跟蹤 //賬戶列表中。
   Derive(path DerivationPath, pin bool) (Account, error)
    
 //SelfDerive設置一個基本賬戶導出路徑,從中錢包嘗試發現非零賬戶,並自動將其添加到跟蹤賬戶列表中。
   SelfDerive(base DerivationPath, chain ethereum.ChainStateReader)
    
 // SignHash 請求錢包來給傳入的hash進行簽名。
   SignHash(account Account, hash []byte) ([]byte, error)
    
  // SignTx 請求錢包對指定的交易進行簽名。
   SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
    
 //SignHashWithPassphrase請求錢包使用給定的passphrase來簽名給定的hash
   SignHashWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error)

   // SignHashWithPassphrase請求錢包使用給定的passphrase來簽名給定的
   SignTxWithPassphrase(account Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
}
複製代碼
後端Backend,Backend是一個錢包提供器。能夠包含一批帳號。他們能夠根據請求籤署交易
type Backend interface {
   // Wallets獲取當前可以查找到的錢包
   Wallets() []Wallet

  // 訂閱建立異步訂閱,以便在後端檢測到錢包的到達或離開時接收通知。
   Subscribe(sink chan<- WalletEvent) event.Subscription
}
複製代碼
manager.go:Manager是一個包含全部東西的帳戶管理工具。能夠和全部的Backends來通訊來簽署交易
type Manager struct {
    // 當前註冊的後端索引
	backends map[reflect.Type][]Backend 
    
    // 錢包更新訂閱全部後端
	updaters []event.Subscription 
    
   // 錢包更新訂閱接收器
	updates  chan WalletEvent           
    
   // 緩存全部錢包從全部註冊後端
	wallets  []Wallet          
    
   // 通知到達/離開的錢包事件
	feed event.Feed 
	
    //退出數據管道 錯誤信息
	quit chan chan error
    
    //讀寫互斥鎖
	lock sync.RWMutex
}
複製代碼

newAccount源碼解讀

瞭解了帳戶的結構之後,咱們來看看和帳戶有關的代碼,由於以太坊源碼的分離性,數據結構的定義和工具方法邏輯實現比較分離,在整個流程的執行中或調用多層。函數

首先當用戶在console也就是控制檯輸入personal.newAccount()會建立一個新的帳戶這個命令的執行流程以下: 1)執行internal/ethapi/api.go文件中的NewAccount方法,返回帳戶地址工具

func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) {
   acc, err := fetchKeystore(s.am).NewAccount(password)
   if err == nil {
      return acc.Address, nil
   }
   return common.Address{}, err
}
複製代碼

internal/ethapi/api.go文件中的NewAccount方法調用fetchKeystore方法從賬戶管理器檢索加密的密鑰存儲庫獲取keystore。

func fetchKeystore(am *accounts.Manager) *keystore.KeyStore {
   return am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
}
複製代碼

internal/ethapi/api.go文件中的NewAccount方法獲取到keystore後經過keystore調用accounts/keystore/keystore.go中的NewAccoun方法獲取account,並將這個帳戶添加到keystore中,返回account

func (ks *KeyStore) NewAccount(passphrase string) (accounts.Account, error) {
   _, account, err := storeNewKey(ks.storage, crand.Reader, passphrase)
   if err != nil {
      return accounts.Account{}, err
   }
   // Add the account to the cache immediately rather
   // than waiting for file system notifications to pick it up.
   ks.cache.add(account)
   ks.refreshWallets()
   return account, nil
}

複製代碼

調用storeNewKey方法建立一個新的帳戶,生成一對公私鑰,經過私鑰以及地址構建一個帳戶

func storeNewKey(ks keyStore, rand io.Reader, auth string) (*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))}}
   if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
      zeroKey(key.PrivateKey)
      return nil, a, err
   }
   return key, a, err
}

複製代碼

Key的生成函數,經過橢圓曲線加密生成的私鑰,生成Key

func newKey(rand io.Reader) (*Key, error) {
   privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand)
   if err != nil {
      return nil, err
   }
   return newKeyFromECDSA(privateKeyECDSA), nil
}
複製代碼

生成公鑰和私鑰對,ecdsa.GenerateKey(crypto.S256(), rand) 以太坊採用了橢圓曲線數字簽名算法(ECDSA)生成一對公私鑰,並選擇的是secp256k1曲線

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 randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
   params := c.Params()
   b := make([]byte, params.BitSize/8+8)
   _, err = io.ReadFull(rand, b)
   if err != nil {
      return
   }

   k = new(big.Int).SetBytes(b)
   n := new(big.Int).Sub(params.N, one)
   k.Mod(k, n)
   k.Add(k, one)
   return
}
複製代碼

以太坊使用私鑰經過 ECDSA算法推導出公鑰,繼而通過 Keccak-256 單向散列函數推導出地址

func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
   id := uuid.NewRandom()
   key := &Key{
      Id:         id,
      Address:    crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
      PrivateKey: privateKeyECDSA,
   }
   return key
}
複製代碼

地址代幣以太坊的20位地址hash

// Address represents the 20 byte address of an Ethereum account.
type Address [AddressLength]byte
複製代碼

整個過程能夠總結爲:

從前控制檯傳入建立帳戶命令

首先建立隨機私鑰

經過私鑰導出公鑰

經過公私鑰導出地址

相關文章
相關標籤/搜索