总所周知以太坊在比特币的基础上加以引用与改进,比特币使用UTXO来表示状态的转移,而以太坊使用帐来表示状态的转移。算法
外部帐户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异步
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)
}
复制代码
type Backend interface {
// Wallets获取当前可以查找到的钱包
Wallets() []Wallet
// 订阅建立异步订阅,以便在后端检测到钱包的到达或离开时接收通知。
Subscribe(sink chan<- WalletEvent) event.Subscription
}
复制代码
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
}
复制代码
了解了帐户的结构之后,咱们来看看和帐户有关的代码,由于以太坊源码的分离性,数据结构的定义和工具方法逻辑实现比较分离,在整个流程的执行中或调用多层。函数
首先当用户在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
复制代码
整个过程能够总结为:
从前控制台传入建立帐户命令
首先建立随机私钥
经过私钥导出公钥
经过公私钥导出地址