以太坊源碼分析(2)Accounts源碼分析與邏輯結(jié)構(gòu)之基本概述與新建賬戶源碼

Accounts源碼分析與邏輯結(jié)構(gòu)1

總所周知以太坊在比特幣的基礎(chǔ)上加以引用與改進(jìn),比特幣使用UTXO來(lái)表示狀態(tài)的轉(zhuǎn)移苛萎,而以太坊使用賬來(lái)表示狀態(tài)的轉(zhuǎn)移桨昙。

accounts包實(shí)現(xiàn)了以太坊客戶端的錢(qián)包和賬戶管理

在以太坊網(wǎng)絡(luò)中存在兩種賬戶:

外部賬戶EOA:一般是屬于個(gè)人或者用戶的賬戶,被私鑰控制沒(méi)有任何代碼與之相關(guān)

內(nèi)部賬戶CA:給智能合約分配的賬戶腌歉,被合約代碼控制蛙酪,且與合約關(guān)聯(lián)

在源碼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賬戶表示發(fā)送交易的序號(hào)翘盖,如果為CA賬戶桂塞,則Nonce表示合約創(chuàng)建的序號(hào)

Balance:表示賬戶的余額,該賬戶地址對(duì)應(yīng)的賬戶余額

Root:存儲(chǔ)merkle樹(shù)的根馍驯,如果為EOA賬戶root為nil

CodeHash:賬戶綁定的EVM Code阁危,如果為EOA則CodeHash為nil

錢(qián)包interface,是指包含了一個(gè)或多個(gè)賬戶的軟件錢(qián)包或者硬件錢(qián)包:
type Wallet interface {
 // URL 用來(lái)獲取這個(gè)錢(qián)包可以訪問(wèn)的規(guī)范路徑汰瘫。它會(huì)被上層使用用來(lái)從所有的后端的錢(qián)包來(lái)排序狂打。
   URL() URL
    
 // 用來(lái)返回一個(gè)文本值用來(lái)標(biāo)識(shí)當(dāng)前錢(qián)包的狀態(tài)。同時(shí)也會(huì)返回一個(gè)error用來(lái)標(biāo)識(shí)錢(qián)包遇到的任何錯(cuò)誤吟吝。
   Status() (string, error)
    
  //Open初始化對(duì)錢(qián)包實(shí)例的訪問(wèn)菱父。如果你open了一個(gè)錢(qián)包,你必須close它。
   Open(passphrase string) error
    
 // Close 釋放由Open方法占用的任何資源浙宜。           
   Close() error
    
  // Accounts用來(lái)獲取錢(qián)包發(fā)現(xiàn)了賬戶列表官辽。對(duì)于分層次的錢(qián)包,這個(gè)列表不會(huì)詳盡的列出所有的賬號(hào)粟瞬,而是只包   //含在帳戶派生期間明確固定的帳戶同仆。
   Accounts() []Account
    
  //包含返回帳戶是否屬于此特定錢(qián)包的一部分。
   Contains(account Account) bool
    
 //Derive嘗試在指定的派生路徑上顯式派生出分層確定性帳戶裙品。如果pin為true俗批,派生帳戶將被添加到錢(qián)包的跟蹤  //帳戶列表中。
   Derive(path DerivationPath, pin bool) (Account, error)
    
 //SelfDerive設(shè)置一個(gè)基本帳戶導(dǎo)出路徑市怎,從中錢(qián)包嘗試發(fā)現(xiàn)非零帳戶岁忘,并自動(dòng)將其添加到跟蹤帳戶列表中。
   SelfDerive(base DerivationPath, chain ethereum.ChainStateReader)
    
 // SignHash 請(qǐng)求錢(qián)包來(lái)給傳入的hash進(jìn)行簽名区匠。
   SignHash(account Account, hash []byte) ([]byte, error)
    
  // SignTx 請(qǐng)求錢(qián)包對(duì)指定的交易進(jìn)行簽名干像。
   SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
    
 //SignHashWithPassphrase請(qǐng)求錢(qián)包使用給定的passphrase來(lái)簽名給定的hash
   SignHashWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error)

   // SignHashWithPassphrase請(qǐng)求錢(qián)包使用給定的passphrase來(lái)簽名給定的
   SignTxWithPassphrase(account Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
}
后端Backend,Backend是一個(gè)錢(qián)包提供器驰弄÷樘可以包含一批賬號(hào)。他們可以根據(jù)請(qǐng)求簽署交易
type Backend interface {
   // Wallets獲取當(dāng)前能夠查找到的錢(qián)包
   Wallets() []Wallet

  // 訂閱創(chuàng)建異步訂閱戚篙,以便在后端檢測(cè)到錢(qián)包的到達(dá)或離開(kāi)時(shí)接收通知五鲫。
   Subscribe(sink chan<- WalletEvent) event.Subscription
}
manager.go:Manager是一個(gè)包含所有東西的賬戶管理工具〔砝蓿可以和所有的Backends來(lái)通信來(lái)簽署交易
type Manager struct {
    // 當(dāng)前注冊(cè)的后端索引
    backends map[reflect.Type][]Backend 
    
    // 錢(qián)包更新訂閱所有后端
    updaters []event.Subscription 
    
   // 錢(qián)包更新訂閱接收器
    updates  chan WalletEvent           
    
   // 緩存所有錢(qián)包從所有注冊(cè)后端
    wallets  []Wallet          
    
   // 通知到達(dá)/離開(kāi)的錢(qián)包事件
    feed event.Feed 
    
    //退出數(shù)據(jù)管道 錯(cuò)誤信息
    quit chan chan error
    
    //讀寫(xiě)互斥鎖
    lock sync.RWMutex
}

newAccount源碼解讀

了解了賬戶的結(jié)構(gòu)以后位喂,我們來(lái)看看和賬戶有關(guān)的代碼,因?yàn)橐蕴辉创a的分離性智亮,數(shù)據(jù)結(jié)構(gòu)的定義和工具方法邏輯實(shí)現(xiàn)比較分離忆某,在整個(gè)流程的執(zhí)行中或調(diào)用多層点待。

首先當(dāng)用戶在console也就是控制臺(tái)輸入personal.newAccount()會(huì)創(chuàng)建一個(gè)新的賬戶這個(gè)命令的執(zhí)行流程如下:
1)執(zhí)行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方法調(diào)用fetchKeystore方法從帳戶管理器檢索加密的密鑰存儲(chǔ)庫(kù)獲取keystore。

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

internal/ethapi/api.go文件中的NewAccount方法獲取到keystore后通過(guò)keystore調(diào)用accounts/keystore/keystore.go中的NewAccoun方法獲取account,并將這個(gè)賬戶添加到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
}

調(diào)用storeNewKey方法創(chuàng)建一個(gè)新的賬戶状原,生成一對(duì)公私鑰,通過(guò)私鑰以及地址構(gòu)建一個(gè)賬戶

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的生成函數(shù)苗踪,通過(guò)橢圓曲線加密生成的私鑰颠区,生成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
}

生成公鑰和私鑰對(duì),ecdsa.GenerateKey(crypto.S256(), rand) 以太坊采用了橢圓曲線數(shù)字簽名算法(ECDSA)生成一對(duì)公私鑰,并選擇的是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
}

以太坊使用私鑰通過(guò) ECDSA算法推導(dǎo)出公鑰通铲,繼而經(jīng)過(guò) Keccak-256 單向散列函數(shù)推導(dǎo)出地址

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

整個(gè)過(guò)程可以總結(jié)為:

從前控制臺(tái)傳入創(chuàng)建賬戶命令

首先創(chuàng)建隨機(jī)私鑰

通過(guò)私鑰導(dǎo)出公鑰

通過(guò)公私鑰導(dǎo)出地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末毕莱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌朋截,老刑警劉巖蛹稍,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異部服,居然都是意外死亡唆姐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)廓八,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)奉芦,“玉大人,你說(shuō)我怎么就攤上這事剧蹂∩Γ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵宠叼,是天一觀的道長(zhǎng)减噪。 經(jīng)常有香客問(wèn)我,道長(zhǎng)车吹,這世上最難降的妖魔是什么筹裕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮窄驹,結(jié)果婚禮上朝卒,老公的妹妹穿的比我還像新娘。我一直安慰自己乐埠,他們只是感情好抗斤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著丈咐,像睡著了一般瑞眼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上棵逊,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天伤疙,我揣著相機(jī)與錄音,去河邊找鬼辆影。 笑死徒像,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蛙讥。 我是一名探鬼主播锯蛀,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼次慢!你這毒婦竟也來(lái)了旁涤?” 一聲冷哼從身側(cè)響起翔曲,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎劈愚,沒(méi)想到半個(gè)月后部默,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡造虎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年傅蹂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片算凿。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡份蝴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出氓轰,到底是詐尸還是另有隱情婚夫,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布署鸡,位于F島的核電站案糙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏靴庆。R本人自食惡果不足惜时捌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望炉抒。 院中可真熱鬧奢讨,春花似錦、人聲如沸焰薄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)塞茅。三九已至亩码,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間野瘦,已是汗流浹背描沟。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缅刽,地道東北人啊掏。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像衰猛,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子刹孔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容