Published: 2019-08-26

go-ethereum源码阅读笔记

近期在学习go-ethereum源码,以便更加清楚地了解其实现,知其所以然。

本文类似于一个学习笔记,可能会有些潦草,会随着学习的进度而更新。

基于git代码版本: release/1.8

Table of Contents

1 accounts

1.1 accounts包

accounts包定义了以太坊账户相关概念和实现。

1.1.1 结构

{struct}Account表示一个以太坊账户

// Account represents an Ethereum account located at a specific location defined
// by the optional URL field.
type Account struct {
        Address common.Address `json:"address"` // Ethereum account address derived from the key
        URL     URL            `json:"url"`     // Optional resource locator within a backend
}

其中{struct}URL是表示一个可选的表示账户的地址

// URL represents the canonical identification URL of a wallet or account.
//
// It is a simplified version of url.URL, with the important limitations (which
// are considered features here) that it contains value-copyable components only,
// as well as that it doesn't do any URL encoding/decoding of special characters.
//
// The former is important to allow an account to be copied without leaving live
// references to the original version, whereas the latter is important to ensure
// one single canonical form opposed to many allowed ones by the RFC 3986 spec.
//
// As such, these URLs should not be used outside of the scope of an Ethereum
// wallet or account.
type URL struct {
        Scheme string // Protocol scheme to identify a capable account backend
        Path   string // Path for the backend to identify a unique entity
}

{interface}Wallet表示一个软件或者硬件钱包,用来管理一个或者一组从同一种子衍生的账户({interface}Account). 接口定义了了一个钱包应该有的方法,比如打开、关闭、查询状态、返回包含的账户、交易签名等

// Wallet represents a software or hardware wallet that might contain one or more
// accounts (derived from the same seed).
type Wallet interface {
        // URL retrieves the canonical path under which this wallet is reachable. It is
        // user by upper layers to define a sorting order over all wallets from multiple
        // backends.
        URL() URL

        // Status returns a textual status to aid the user in the current state of the
        // wallet. It also returns an error indicating any failure the wallet might have
        // encountered.
        Status() (string, error)

        // Open initializes access to a wallet instance. It is not meant to unlock or
        // decrypt account keys, rather simply to establish a connection to hardware
        // wallets and/or to access derivation seeds.
        //
        // The passphrase parameter may or may not be used by the implementation of a
        // particular wallet instance. The reason there is no passwordless open method
        // is to strive towards a uniform wallet handling, oblivious to the different
        // backend providers.
        //
        // Please note, if you open a wallet, you must close it to release any allocated
        // resources (especially important when working with hardware wallets).
        Open(passphrase string) error

        // Close releases any resources held by an open wallet instance.
        Close() error

        // Accounts retrieves the list of signing accounts the wallet is currently aware
        // of. For hierarchical deterministic wallets, the list will not be exhaustive,
        // rather only contain the accounts explicitly pinned during account derivation.
        Accounts() []Account

        // Contains returns whether an account is part of this particular wallet or not.
        Contains(account Account) bool

        // Derive attempts to explicitly derive a hierarchical deterministic account at
        // the specified derivation path. If requested, the derived account will be added
        // to the wallet's tracked account list.
        Derive(path DerivationPath, pin bool) (Account, error)

        // SelfDerive sets a base account derivation path from which the wallet attempts
        // to discover non zero accounts and automatically add them to list of tracked
        // accounts.
        //
        // Note, self derivaton will increment the last component of the specified path
        // opposed to decending into a child path to allow discovering accounts starting
        // from non zero components.
        //
        // You can disable automatic account discovery by calling SelfDerive with a nil
        // chain state reader.
        SelfDerive(base DerivationPath, chain ethereum.ChainStateReader)

        // SignHash requests the wallet to sign the given hash.
        //
        // It looks up the account specified either solely via its address contained within,
        // or optionally with the aid of any location metadata from the embedded URL field.
        //
        // If the wallet requires additional authentication to sign the request (e.g.
        // a password to decrypt the account, or a PIN code o verify the transaction),
        // an AuthNeededError instance will be returned, containing infos for the user
        // about which fields or actions are needed. The user may retry by providing
        // the needed details via SignHashWithPassphrase, or by other means (e.g. unlock
        // the account in a keystore).
        SignHash(account Account, hash []byte) ([]byte, error)

        // SignTx requests the wallet to sign the given transaction.
        //
        // It looks up the account specified either solely via its address contained within,
        // or optionally with the aid of any location metadata from the embedded URL field.
        //
        // If the wallet requires additional authentication to sign the request (e.g.
        // a password to decrypt the account, or a PIN code to verify the transaction),
        // an AuthNeededError instance will be returned, containing infos for the user
        // about which fields or actions are needed. The user may retry by providing
        // the needed details via SignTxWithPassphrase, or by other means (e.g. unlock
        // the account in a keystore).
        SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)

        // SignHashWithPassphrase requests the wallet to sign the given hash with the
        // given passphrase as extra authentication information.
        //
        // It looks up the account specified either solely via its address contained within,
        // or optionally with the aid of any location metadata from the embedded URL field.
        SignHashWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error)

        // SignTxWithPassphrase requests the wallet to sign the given transaction, with the
        // given passphrase as extra authentication information.
        //
        // It looks up the account specified either solely via its address contained within,
        // or optionally with the aid of any location metadata from the embedded URL field.
        SignTxWithPassphrase(account Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
}


{interface}Backend用来管理一组{interface}Wallet

// Backend is a "wallet provider" that may contain a batch of accounts they can
// sign transactions with and upon request, do so.
type Backend interface {
        // Wallets retrieves the list of wallets the backend is currently aware of.
        //
        // The returned wallets are not opened by default. For software HD wallets this
        // means that no base seeds are decrypted, and for hardware wallets that no actual
        // connection is established.
        //
        // The resulting wallet list will be sorted alphabetically based on its internal
        // URL assigned by the backend. Since wallets (especially hardware) may come and
        // go, the same wallet might appear at a different positions in the list during
        // subsequent retrievals.
        Wallets() []Wallet

        // Subscribe creates an async subscription to receive notifications when the
        // backend detects the arrival or departure of a wallet.
        Subscribe(sink chan<- WalletEvent) event.Subscription
}


{[]uint32}DerivationPath 定义了BIP-32和BIP-44规定的分层确定性钱包地址路径

// DerivationPath represents the computer friendly version of a hierarchical
// deterministic wallet account derivaion path.
//
// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
// defines derivation paths to be of the form:
//
//   m / purpose' / coin_type' / account' / change / address_index
//
// The BIP-44 spec https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
// defines that the `purpose` be 44' (or 0x8000002C) for crypto currencies, and
// SLIP-44 https://github.com/satoshilabs/slips/blob/master/slip-0044.md assigns
// the `coin_type` 60' (or 0x8000003C) to Ethereum.
//
// The root path for Ethereum is m/44'/60'/0'/0 according to the specification
// from https://github.com/ethereum/EIPs/issues/84, albeit it's not set in stone
// yet whether accounts should increment the last component or the children of
// that. We will go with the simpler approach of incrementing the last component.
type DerivationPath []uint32

1.1.2 TODO 方法实现

1.2 keystore包

keystore包里定义了以“keystore”文件方式存储以太坊账户信息的相关结构和实现。

1.2.1 结构

{interface}keyStore定义了一个“keystore”实现应该有的方法,包括获取/存储{struct}Key,获取文件完整目录等。

type keyStore interface {
        // Loads and decrypts the key from disk.
        GetKey(addr common.Address, filename string, auth string) (*Key, error)
        // Writes and encrypts the key.
        StoreKey(filename string, k *Key, auth string) error
        // Joins filename with the key directory unless it is already absolute.
        JoinPath(filename string) string
}

这里面的{struct}Key表示一个账户最本质的内容——私钥,以及根据私钥计算的地址等。

type Key struct {
        Id uuid.UUID // Version 4 "random" for unique id not derived from key data
        // to simplify lookups we also store the address
        Address common.Address
        // we only store privkey as pubkey/address can be derived from it
        // privkey in this struct is always in plaintext
        PrivateKey *ecdsa.PrivateKey
}

{struct}keyStorePassphrase和{struct}keyStorePlain是对{interface}keyStore的两个实现。

其中,{struct}keyStorePassphrase是以密码passphrase的方式来存储私钥成文件,而{struct}keyStorePlain是直接不加密存储成文件。

type keyStorePassphrase struct {
        keysDirPath string
        scryptN     int
        scryptP     int
        // skipKeyFileVerification disables the security-feature which does
        // reads and decrypts any newly created keyfiles. This should be 'false' in all
        // cases except tests -- setting this to 'true' is not recommended.
        skipKeyFileVerification bool
}

type keyStorePlain struct {
        keysDirPath string
}

{struct}keystoreWallet实现了{interface}accounts.Wallet接口。 一个keystore钱包实际上只包含了一个账户

// keystoreWallet implements the accounts.Wallet interface for the original
// keystore.
type keystoreWallet struct {
        account  accounts.Account // Single account contained in this wallet
        keystore *KeyStore        // Keystore where the account originates from
}

{struct}KeyStore是整个keystore包的主要逻辑实现:

  1. 管理一个私钥如何存储在硬盘文件上(通过绑定的{interface}keyStore)
  2. 管理缓存来避免总是读写文件(通过绑定的{struct}accountCache)
  3. 管理一个ketstore目录下的所有的account
  4. 管理账户的新建导入导出删除等
// KeyStore manages a key storage directory on disk.
type KeyStore struct {
        storage  keyStore                     // Storage backend, might be cleartext or encrypted
        cache    *accountCache                // In-memory account cache over the filesystem storage
        changes  chan struct{}                // Channel receiving change notifications from the cache
        unlocked map[common.Address]*unlocked // Currently unlocked account (decrypted private keys)

        wallets     []accounts.Wallet       // Wallet wrappers around the individual key files
        updateFeed  event.Feed              // Event feed to notify wallet additions/removals
        updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
        updating    bool                    // Whether the event notification loop is running

        mu sync.RWMutex
}


{struct}accountCache是keystore目录下所有账户的一个索引和管理方式

// accountCache is a live index of all accounts in the keystore.
type accountCache struct {
        keydir   string
        watcher  *watcher
        mu       sync.Mutex
        all      accountsByURL
        byAddr   map[common.Address][]accounts.Account
        throttle *time.Timer
        notify   chan struct{}
        fileC    fileCache
}


// fileCache是文件变动时间的一个记录
// fileCache is a cache of files seen during scan of keystore.
type fileCache struct {
        all     mapset.Set // Set of all files from the keystore folder
        lastMod time.Time  // Last time instance when a file was modified
        mu      sync.RWMutex
}

1.2.2 TODO 方法实现

1.3 结构关系图

下面这张图展示来accounts包和keystore包结构体和接口之间的关系: read-go-ethereum-02.png

2 p2p

table.go文件里对主要实现了一个Table结构,该结构用来管理连接的节点的信息。

table.go所属的是discover包,discover包实现了一种类似kad协议的节点发现协议。

当实例化一个Table的时候,会创建一个 loop() 的goroutine, loop用来管理节点的刷新、验证等工作,下面我整理了下loop的大体逻辑图:

read-go-ethereum-01.png

3 End

Modified: 2019-08-26

Author: Nisen

Email: imnisen@163.com