go-ethereum源碼分析(1) -- 交易、區塊、區塊鏈

來源:互聯網
上載者:User

前言

這段時間一直在做區塊鏈公鏈項目開發,主要是基於bitcoin-core源碼進行開發,理解區塊鏈原理及基礎概念;個人同時對於以太坊也感興趣,所以準備拿go-ethereum學習一番,過程會持續幾個月,這裡把學習筆記記錄下來;本人現在對ethereum也是菜鳥小白,這篇文章主要是針對go-ethereum小白,大牛就請繞過吧。

現在開始吧

區塊鏈基本概念:交易、區塊、區塊鏈,是區塊鏈中的核心基礎,今天就從這幾個概念入手分析吧。(這裡忍不住多說幾句,任何科學領域基礎概念真的很重要,工作中遇到的很多問題都是因為基本概念理解不到位,解決問題時需要把基本概念重新理解一遍;依然記得若干年之前南京大學徐家福教授的演講,一位同學問怎麼才能學好電腦,徐教授什麼話也沒說,拿起粉筆在黑板上顫抖著手寫到:“基礎概念,基礎概念,基礎概念”,大家知道基礎知識的重要性了吧。如果大家不知道徐教授就去百度吧,新中國電腦領域的創始人之一,學術界流行一句話“北有楊芙清(清華的),南有徐家福(南大的)”)。

交易

core/types/transaction.go

type Transaction struct {

        data txdata //交易的內容,在txdata類型中儲存

        // caches 以下三個欄位是否只在memory裡儲存?

        hash atomic.Value // 交易的hash

        size atomic.Value // 交易的大小

        from atomic.Value // 交易的發起方

}

type txdata struct {

        AccountNonce uint64 //  account nonce?幹什麼的,不知道

        Price        *big.Int  // gasprice

        GasLimit    uint64 //  gaslimit,我們知道在寫智能合約時設定gas  limit,能夠防止程式異常消耗太多的gas

        Recipient    *common.Address  //  交易的receiver, nil means contract creation

        Amount      *big.Int      //交易以太的數量??有待確認

        Payload      []byte        //交易可以攜帶payload,智能合約的位元組碼存放在此??有待確認

        // Signature values,和簽名相關,暫不深究

        V *big.Int

        R *big.Int

        S *big.Int

        // This is only used when marshaling to JSON.

        Hash *common.Hash

}

以上是交易的結構體定義,下面看一下和交易相關的函數和方法(golang 既有函數的概念,又有方法的概念)。

func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte)

        *Transaction {

        return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data)

}

func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {

      return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data)

}

上面的函數,NewTransaction是建立普通的交易,NewContractCreation是用來建立智能合約,兩者都調用了內建函式(非匯出)newTransaction,區別很明顯就是NewContractCreation把交易接收地址設定為nil。

func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte)

        *Transaction {

        if len(data) > 0 {

        data = common.CopyBytes(data)

        }

        d := txdata{

                AccountNonce: nonce,

                Recipient:    to,

                Payload:      data,

                Amount:      new(big.Int),

                GasLimit:    gasLimit,

              Price:        new(big.Int),

              V:            new(big.Int),

                R:            new(big.Int),

                S:            new(big.Int),

        }

        if amount != nil {

                d.Amount.Set(amount)

        }

        if gasPrice != nil {

                d.Price.Set(gasPrice)

        }

        return &Transaction{data: d}

}

以上函數簡單,先建立txdata對象,然後初始化資料成員。

下面的EncodeRLP與DecodeRLP主要負責交易的 RLP編解碼。

// EncodeRLP implements rlp.Encoder

func (tx *Transaction) EncodeRLP(w io.Writer) error {

        return rlp.Encode(w, &tx.data)

}

// DecodeRLP implements rlp.Decoder

func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {

        _, size, _ := s.Kind()

        err := s.Decode(&tx.data)

        if err == nil {

                tx.size.Store(common.StorageSize(rlp.ListSize(size)))

        }

        return err

}

這裡先簡單介紹幾個與交易相關的函數和方法,更多的函數和方法大家可以查閱原始碼。不展開的原因我是想先追求整體理解,然後再是具體實現。

區塊

core/types/block.go

先看一下區塊頭的資料結構:

// Header represents a block header in the Ethereum blockchain.

type Header struct {

        ParentHash  common.Hash //上一個區塊的hash,用來把區塊組織成鏈

        UncleHash  common.Hash  //unclehash,理解不是太深入,後面再說

        Coinbase    common.Address  //POW共識演算法coinbase交易對應的地址

        Root        common.Hash    // state trie  tree Root

        TxHash      common.Hash    // transactions  Root

        ReceiptHash common.Hash    // receipts  Root

        Bloom      Bloom        //應該是bloom filter,暫不深入

        Difficulty  *big.Int    //POW共識演算法的難度值,會隨著區塊鏈高度進行調整

        Number      *big.Int    // 區塊高度

        GasLimit    uint64    // 區塊頭的gaslimit??還不太清楚是什麼作用

        GasUsed    uint64    //整個區塊中交易的消耗的gas?有待確認

        Time        *big.Int    //出塊時間

        Extra      []byte 

        MixDigest  common.Hash  //暫時不知道用來幹什麼

        Nonce      BlockNonce  //POW共識演算法中的nonce

}

下面的方法擷取block header 的hash值:

// Hash returns the block hash of the header, which is simply the keccak256 hash of its

// RLP encoding.

func (h *Header) Hash() common.Hash {

        return rlpHash(h)

}

// HashNoNonce returns the hash which is used as input for the proof-of-work search.

func (h *Header) HashNoNonce() common.Hash {

        return rlpHash([]interface{}{

                h.ParentHash,

                h.UncleHash,

                h.Coinbase,

                h.Root,

                h.TxHash,

                h.ReceiptHash,

                h.Bloom,

                h.Difficulty,

                h.Number,

                h.GasLimit,

                h.GasUsed,

                h.Time,

                h.Extra,

        })

}

func rlpHash(x interface{}) (h common.Hash) {

        hw := sha3.NewKeccak256()

      rlp.Encode(hw, x)

      hw.Sum(h[:0])

      return h

}

以下是區塊體的資料結構:

// Body is a simple (mutable, non-safe) data container for storing and moving

// a block's data contents (transactions and uncles) together.

type Body struct {

        Transactions []*Transaction //區塊體中的所有交易

        Uncles      []*Header//還不太明白這個欄位作用,難道和分叉有關?

}

區塊的資料結構如下:

// Block represents an entire block in the Ethereum blockchain.

type Block struct {

        header      *Header // 區塊頭指標

        uncles      []*Header

        transactions Transactions  //區塊體中的交易

        // caches

        hash atomic.Value

        size atomic.Value

        // Td is used by package core to store the total difficulty

        // of the chain up to and including the block.

        td *big.Int //鏈上的總體難度值

        // These fields are used by package eth to track

        // inter-peer block relay.

        //如注釋,這兩個欄位用來追蹤節點之間區塊的轉寄

        ReceivedAt  time.Time //區塊收到的時間

        ReceivedFrom interface{} //標記本區塊是從哪個對端節點收到的

}

// StorageBlock defines the RLP encoding of a Block stored in the

// state database. The StorageBlock encoding contains fields that

// would otherwise need to be recomputed.

type StorageBlock Block

// "external" block encoding. used for eth protocol, etc.

type extblock struct {

        Header *Header

        Txs    []*Transaction

        Uncles []*Header

}

//以上的Block資料結構是在記憶體中儲存的,在資料庫中實際儲存的結構體是 storageblock,定義如下:

// "storage" block encoding. used for database.

type storageblock struct {

        Header *Header

        Txs    []*Transaction

        Uncles []*Header

        TD    *big.Int

}

下面簡單分析下建立block的函數NewBlock:

// NewBlock creates a new block. The input data is copied,

// changes to header and to the field values will not affect the block.

// The values of TxHash, UncleHash, ReceiptHash and Bloom in header

// are ignored and set to values derived from the given txs, uncles  and receipts.

func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block {

        b := &Block{header: CopyHeader(header), td: new(big.Int)}  //建立block 對象

        // TODO: panic if len(txs) != len(receipts)

        if len(txs) == 0 {

                b.header.TxHash = EmptyRootHash //如果區塊中沒有交易,TXHash賦值為 EmptyRootHash

        } else {

                b.header.TxHash = DeriveSha(Transactions(txs))

                b.transactions = make(Transactions, len(txs))

                copy(b.transactions, txs)  //建立並拷貝tx的副本

        }

        if len(receipts) == 0 {

                b.header.ReceiptHash = EmptyRootHash

      } else {

                b.header.ReceiptHash = DeriveSha(Receipts(receipts))

                b.header.Bloom = CreateBloom(receipts)

        }

        if len(uncles) == 0 {

                b.header.UncleHash = EmptyUncleHash

        } else {

                b.header.UncleHash = CalcUncleHash(uncles)

                b.uncles = make([]*Header, len(uncles))

                for i := range uncles {

                        b.uncles[i] = CopyHeader(uncles[i])

                }

        }

        return b

}

// NewBlockWithHeader creates a block with the given header data. The

// header data is copied, changes to header and to the field values

// will not affect the block.

func NewBlockWithHeader(header *Header) *Block {

        return &Block{header: CopyHeader(header)}

}

// DecodeRLP與 EncodeRLP方法是block的RLP編解碼的具體實現:

// DecodeRLP decodes the Ethereum

func (b *Block) DecodeRLP(s *rlp.Stream) error {

        var eb extblock

        _, size, _ := s.Kind()

        if err := s.Decode(&eb); err != nil {

                return err

        }

        b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs

        b.size.Store(common.StorageSize(rlp.ListSize(size)))

        return nil

}

// EncodeRLP serializes b into the Ethereum RLP block format.

func (b *Block) EncodeRLP(w io.Writer) error {

        return rlp.Encode(w, extblock{

                Header: b.header,

                Txs:    b.transactions,

                Uncles: b.uncles,

        })

}

這部分簡單介紹了區塊頭、區塊體、區塊、資料庫區塊的資料結構和方法,詳情請參考源碼。

區塊鏈

core/blockchain.go

介紹了交易、區塊頭、區塊後,我們來看下區塊鏈的資料結構:

// BlockChain represents the canonical chain given a database with a genesis

// block. The Blockchain manages chain imports, reverts, chain reorganisations.

// Importing blocks in to the block chain happens according to the set of rules

// defined by the two stage Validator. Processing of blocks is done using the

// Processor which processes the included transaction. The validation of the state

// is done in the second part of the Validator. Failing results in aborting of the import.

//

// The BlockChain also helps in returning blocks from **any** chain included

// in the database as well as blocks that represents the canonical chain. It's

// important to note that GetBlock can return any block and does not need to be

// included in the canonical one where as GetBlockByNumber always represents the

// canonical chain.

type BlockChain struct {

        chainConfig *params.ChainConfig // Chain & network configuration  字面意思是鏈和網路的配置,這些配置暫不深究

        cacheConfig *CacheConfig        // Cache configuration for pruning

        db    ethdb.Database // Low level persistent database to store final content in,底層level db

        triegc *prque.Prque  // Priority queue mapping block numbers to tries to gc  不知道什麼作用

        gcproc time.Duration  // Accumulates canonical block processing for trie dumping

        hc            *HeaderChain

        rmLogsFeed    event.Feed  //  event.Feed存放著訂閱者資訊,blockchain有事件發生時通知訂閱者

        chainFeed    event.Feed

        chainSideFeed event.Feed

        chainHeadFeed event.Feed

        logsFeed      event.Feed

        scope        event.SubscriptionScope

        genesisBlock  *types.Block // 創世區塊指標

        mu      sync.RWMutex // global mutex for locking chain operations

        chainmu sync.RWMutex // blockchain insertion lock

        procmu  sync.RWMutex // block processor lock

        checkpoint   int   // checkpoint counts towards the new checkpoint

        currentBlock    atomic.Value // 當前區塊

        currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)

        stateCache  state.Database // State database to reuse between imports (contains state cache)

        bodyCache    *lru.Cache    // Cache for the most recent block bodies

        bodyRLPCache *lru.Cache    // Cache for the most recent block bodies in RLP encoded format

        blockCache  *lru.Cache    // Cache for the most recent entire blocks

        futureBlocks *lru.Cache    // future blocks are blocks added for later processing

        quit    chan struct{} // blockchain quit channel

        running int32        // running must be called atomically

        // procInterrupt must be atomically called

        procInterrupt int32          // interrupt signaler for block processing

        wg            sync.WaitGroup // chain processing wait group for shutting down

        engine    consensus.Engine // 共識演算法引擎,不同演算法識別了Engine介面

        processor Processor  // block processor interface,非常重要的資料成員

        validator Validator // block and state validator interface,非常重要的資料成員

        vmConfig  vm.Config  // 虛擬機器的配置

        badBlocks *lru.Cache // Bad block cache

}

以上就是區塊鏈的資料結構,好多資料成員不明白什麼作用,沒有問題直接跳過,隨著分析的不斷深入,我們就會理解。

// NewBlockChain returns a fully initialised block chain using information available in the database. It initialises the default Ethereum Validator and Processor.

func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {

        //  如果 cache config 為空白,建立對象

        if cacheConfig == nil {

                cacheConfig = &CacheConfig{

                TrieNodeLimit: 256 * 1024 * 1024,

                TrieTimeLimit: 5 * time.Minute,

                 }

        }

        //  初始化各種資料成員

        bodyCache, _ := lru.New(bodyCacheLimit)

        bodyRLPCache, _ := lru.New(bodyCacheLimit)

        blockCache, _ := lru.New(blockCacheLimit)

        futureBlocks, _ := lru.New(maxFutureBlocks)

        badBlocks, _ := lru.New(badBlockLimit)

        //  建立區塊鏈對象

        bc := &BlockChain{

                chainConfig:  chainConfig,

                cacheConfig:  cacheConfig,

                db:          db,

                triegc:      prque.New(),

                stateCache:  state.NewDatabase(db),

                quit:        make(chan struct{}),

                bodyCache:    bodyCache,

                bodyRLPCache: bodyRLPCache,

                blockCache:  blockCache,

                futureBlocks: futureBlocks,

                engine:      engine,

                vmConfig:    vmConfig,

                badBlocks:    badBlocks,

        }

        //  建立區塊鏈的驗證器和處理器,後面會看到這兩個資料成員非常重要

        bc.SetValidator(NewBlockValidator(chainConfig, bc, engine))

        bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine))

        var err error

        bc.hc, err = NewHeaderChain(db, chainConfig, engine,         bc.getProcInterrupt)

        if err != nil {

                return nil, err

        }

        //  擷取創世區塊

        bc.genesisBlock = bc.GetBlockByNumber(0)

        if bc.genesisBlock == nil {

                     return nil, ErrNoGenesis

        }

        if err := bc.loadLastState(); err != nil {

                return nil, err

        }

        // Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain

        for hash := range BadHashes {

                if header := bc.GetHeaderByHash(hash); header != nil {

                // get the canonical block corresponding to the offending header's number

                headerByNumber :=                     bc.GetHeaderByNumber(header.Number.Uint64())

                // make sure the headerByNumber (if present) is in our current canonical chain

                if headerByNumber != nil && headerByNumber.Hash() ==                     header.Hash() {

                        log.Error("Found bad hash, rewinding chain", "number",                         header.Number, "hash", header.ParentHash)

                        bc.SetHead(header.Number.Uint64() - 1)

                        log.Error("Chain rewind was successful, resuming normal operation")

                }

                }

        }

        // Take ownership of this particular state

        go bc.update()

        return bc, nil

}

以上就是初始化區塊鏈的方法,BlockChain還有好多重要方法,由於篇幅有限,這裡就不一一展開了,以後分析需要時回來再看。下面簡單標記了幾個重要方法,詳情可以參考代碼。

// loadLastState loads the last known chain state from the database. This method

// assumes that the chain manager mutex is held.

func (bc *BlockChain) loadLastState() error {

        ......

}

// SetHead rewinds the local chain to a new head. In the case of headers, everythingabove the new head will be deleted and the new one set. In the case of blocks though, the head may be further rewound if block bodies are missing (non-archive nodes after a fast sync).

func (bc *BlockChain) SetHead(head uint64) error {

        ......

}


// CurrentBlock retrieves the current head block of the canonical chain. The  block is retrieved from the blockchain's internal cache.

func (bc *BlockChain) CurrentBlock() *types.Block {

        ......

}

// SetProcessor sets the processor required for making state modifications.

func (bc *BlockChain) SetProcessor(processor Processor) {

        ......

}

// SetValidator sets the validator which is used to validate incoming blocks.

func (bc *BlockChain) SetValidator(validator Validator) {

        ......

}

// Validator returns the current validator.

func (bc *BlockChain) Validator() Validator {

        ......

}

// Processor returns the current processor.

func (bc *BlockChain) Processor() Processor {

        ......

}

總結:

這篇文章簡單介紹了go ethetheum源碼中交易、區塊、區塊鏈的資料結構以及初始化方法,希望大家有所瞭解。下一篇文章將會介紹交易池,看一下交易是如何被打包成區塊的,區塊是如何被廣播到網路上的。由於筆者也是菜鳥,很多概念理解不深入,不理解的概念暫且放過,分析深入後就會理解的。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.