以太坊源码分析(2)Accounts源码分析与逻辑结构之基本概述与新建账户源码

Accounts源码分析与逻辑结构1

总所周知以太坊在比特币的基础上加以引用与改进,比特币使用UTXO来表示状态的转移,而以太坊使用账来表示状态的转移。

accounts包实现了以太坊客户端的钱包和账户管理

在以太坊网络中存在两种账户:

外部账户EOA:一般是属于个人或者用户的账户,被私钥控制没有任何代码与之相关

内部账户CA:给智能合约分配的账户,被合约代码控制,且与合约关联

在源码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为nil

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

整个过程可以总结为:

从前控制台传入创建账户命令

首先创建随机私钥

通过私钥导出公钥

通过公私钥导出地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容