1. 程式人生 > >【以太坊原始碼】I.區塊和交易,合約和虛擬機器

【以太坊原始碼】I.區塊和交易,合約和虛擬機器

最近在看以太坊(Ethereum)的原始碼, 初初看出點眉目。 區塊鏈是近年熱點之一,面向大眾讀者介紹概念的文章無數,有興趣的朋友可自行搜尋。我會從原始碼實現入手,較系統的介紹一下以太坊的系統設計和協議實現等,希望能提供有一定深度的內容,歡迎有興趣的朋友多多討論。

注:1.原始碼在github上, 分C++和Golang兩個版本,這裡我選擇的是Go語言版(github.com/ethereum/go-ethereum),以下文中提到的Ethereum 程式碼部分,如無特別說明,均指go-ethereum; 2.github 主幹程式碼還在持續更新中,所以此文中摘錄的程式碼將來可能會跟讀者的本地版本有所不同,如有差異我會作相應修改。

1. 基本概念

1.1 SHA-3雜湊加密,RLP編碼

Ethereum 程式碼裡雜湊(hash)無處不在,許許多多的型別物件通過給定的雜湊演算法,可以得到一個雜湊值。注意,演算法中所使用的雜湊函式是不可逆的,即對於h = hash(x), 僅僅通過雜湊運算的結果h 無法作逆運算得到輸入x。雜湊值在數學上的唯一性使得它可以用作某個物件的全域性唯一識別符號。

Ethereum 中用到的雜湊函式全部採用SHA-3(Secure Hash Algorithm 3,wikipedia)。SHA-3在2015年8月由美國標準技術協會(NIST)正式釋出,作為Secure Hash Algorithm家族的最新一代標準,它相比於SHA-2和SHA-1,採用了完全不同的設計思路,效能也比較好。需要注意的是,SHA-2目前並沒有出現被成功攻克的案例,SHA-3也沒有要立即取代SHA-2的趨勢,NIST只是考慮到SHA-1有過被攻克的案例,未雨綢繆的徵選了採用全新結構和思路的SHA-3來作為一種最新的SHA方案。

RLP(Recursive Length Prefix)編碼,其定義可見wiki,它可以將一個任意巢狀的位元組陣列([]byte),編碼成一個“展平”無巢狀的[]byte。1 byte取值範圍0x00 ~ 0xff,可以表示任意字元,所以[]byte可以線性的表示任意的資料。最簡單比如一個字串,如果每個字元用ASCII碼的二進位制表示,整個字串就變成一個[]byte。 RLP 編碼其實提供了一種序列化的編碼方法,無論輸入是何種巢狀形式的元素或陣列,編碼輸出形式都是[]byte。RLP是可逆的,它提供了互逆的編碼、解碼方法。

Ethereum 中具體使用的雜湊演算法,就是對某個型別物件的RLP編碼值做了SHA3雜湊運算,可稱為RLP Hash

。 Ethereum 在底層儲存中特意選擇了專門儲存和讀取[k, v] 鍵值對的第三方資料庫,[k, v] 中的v 就是某個結構體物件的RLP編碼值([]byte),k大多數情況就是v的RLP編碼後的SHA-3雜湊值

1.2 常用資料型別 雜湊值和地址

兩個最常用的自定義資料型別common.Hash用來表示雜湊值,common.Address表示地址

  1. # /commons/types.go
  2. const (
  3. HashLength = 32
  4. AddressLength = 20
  5. )
  6. type Hash [HashLength]byte
  7. type Address [AddressLength]byte

在Ethereum 程式碼裡,所有用到的雜湊值,都使用該Hash型別,長度為32bytes,即256 bits;Ethereum 中所有跟帳號(Account)相關的資訊,比如交易轉帳的轉出帳號(地址)和轉入帳號(地址),都會用該Address型別表示,長度20bytes。

big.Int是golang提供的資料型別,用來處理比較大的整型數,當然它也可以處理諸如64bit,32bit的常用整數。

  1. # /go-1.x/src/math/big/int.go
  2. package big
  3. type Int struct {
  4. neg bool // sign, whether negaive
  5. abs nat // absolute value of integer
  6. }

big.Int是一個結構體(struct),相當於C++中的class,所以每次新建big.Int時可以用 x := new(big.Int), 返回一個指標。注意對Int的算術操作,要使用該物件的成員函式,比如Add():

func(z *Int) Add(x, y *Int) *Int   // Add sets z to sum x+y and returns z

 Ethereum 程式碼中, 很多整型變數的型別都選用big.Int,比如Gas和Ether。

1.3 汽油(Gas)和以太幣(Ether)

Gas, 是Ethereum裡對所有活動進行消耗資源計量的單位。這裡的活動是泛化的概念,包括但不限於:轉帳,合約的建立,合約指令的執行,執行中記憶體的擴充套件等等。所以Gas可以想象成現實中的汽油或者燃氣。

Ether, 是Ethereum世界中使用的數字貨幣,也就是常說的以太幣。如果某個帳號,Address A想要發起一個交易,比如一次簡單的轉帳,即向 Address B 傳送一筆金額H,那麼Address A 本身擁有的Ether,除了轉帳的數額H之外,還要有額外一筆金額用以支付交易所耗費的Gas。

如果可以實現Gas和Ether之間的換算,那麼Ethereum系統裡所有的活動,都可以用Ether來計量。這樣,Ether就有了點一般等價物,也就是貨幣的樣子。

1.4 區塊是交易的集合

區塊(Block)是Ethereum的核心結構體之一。在整個區塊鏈(BlockChain)中,一個個Block是以單向連結串列的形式相互關聯起來的。Block中帶有一個Header(指標), Header結構體帶有Block的所有屬性資訊,其中的ParentHash 表示該區塊的父區塊雜湊值, 亦即Block之間關聯起來的前向指標。只不過要想得到父區塊(parentBlock)物件,直接解析這個ParentHash是不夠的, 而是要將ParentHash同其他字串([]byte)組合成合適的key([]byte), 去kv資料庫裡查詢相應的value才能解析得到。 Block和Header的部分成員變數定義如下:

  1. # /core/types/block.go
  2. type Block struct {
  3. header *Header
  4.     transactions Transactions  // type Transactions []*Transaction
  5. ...
  6. }
  7. type Header struct {
  8.     ParentHash common.Hash
  9.     Number *big.Int
  10.     ...
  11. }

Header的整型成員Number表示該區塊在整個區塊鏈(BlockChain)中所處的位置,每一個區塊相對於它的父區塊,其Number值是+1。這樣,整個區塊鏈會存在一個原始區塊,即創世塊(GenesisBlock), 它的Number是0,由系統自然生成而不必去額外挖掘(mine)。Block和BlockChain的實現細節,之後會有更詳細的討論。

Block中還有一個Tranction(指標)陣列,這是我們這裡關注的。Transaction(簡稱tx),是Ethereum裡標示一次交易的結構體, 它的成員變數包括轉帳金額,轉入方地址等等資訊。Transaction的完整宣告如下:

  1. # /core/types/transaction.go
  2. type Transaction struct {
  3. data txdata
  4. hash, size, from atomic.Value // for cache
  5. }
  6. type txdata struct {
  7. AccountNonce uint64
  8. Price *big.Int
  9. GasLimit *big.Int
  10. Recipient *common.Address
  11. Amount *big.Int
  12. Payload []byte
  13. V, R, S *big.Int // for signature
  14. Hash *common.Hash // for marshaling
  15. }
每個tx都聲明瞭自己的(Gas)Price 和 GasLimit。 Price指的是單位Gas消耗所折抵的Ether多少,它的高低意味著執行這個tx有多麼昂貴。GasLimit 是該tx執行過程中所允許消耗資源的總上限,通過這個值,我們可以防止某個tx執行中出現惡意佔用資源的問題,這也是Ethereum中有關安全保護的策略之一。擁有獨立的Price和GasLimit, 也意味著每個tx之間都是相互獨立的。

轉帳轉入方地址Recipient可能為空(nil),這時在後續執行tx過程中,Ethereum 需要建立一個地址來完成這筆轉帳。Payload是重要的資料成員,它既可以作為所建立合約的指令陣列,其中每一個byte作為一個單獨的虛擬機器指令;也可以作為資料陣列,由合約指令進行操作。合約由以太坊虛擬機器(Ethereum Virtual Machine, EVM)建立並執行。

細心的朋友在這裡會有個疑問,為何交易的定義裡沒有宣告轉帳的轉出方地址? 問的好,tx 的轉帳轉出方地址確實沒有如轉入方一樣被顯式的宣告出來,而是被加密隱藏起來了,在Ethereum裡這個轉出方地址是機密,不能直接暴露。這個對tx加密的環節,在Ethereum裡被稱為簽名(sign), 關於它的實現細節容後再述。

2. 交易的執行

Block 型別的基本目的之一,就是為了執行交易。狹義的交易可能僅僅是一筆轉帳,而廣義的交易同時還會支援許多其他的意圖。Ethereum 中採用的是廣義交易概念。按照其架構設計,交易的執行可大致分為內外兩層結構第一層是虛擬機器外,包括執行前將Transaction型別轉化成Message,建立虛擬機器(EVM)物件,計算一些Gas消耗,以及執行交易完畢後建立收據(Receipt)物件並返回等;第二層是虛擬機器內,包括執行轉帳,和建立合約並執行合約的指令陣列。

2.1 虛擬機器外

2.1.1 入口和返回值

執行tx的入口函式是StateProcessor的Process()函式,其實現程式碼如下:

  1. # /core/state_processor.go
  2. func(p *StateProcessor)Process(block *Block, statedb *StateDB, cfg vm.Config)(types.Receipts, []*types.Log, *big.Int, error) {
  3. var {
  4. receipts types.Receipts
  5. totalUsedGas = big.NewInt(0)
  6. header = block.Header()
  7. allLogs  []*types.Log
  8. gp = new(GasPool).AddGas(block.GasLimit())
  9. }
  10. ...
  11. for i, tx := range block.Transactions() {
  12. statedb.Prepare(tx.Hash(), block.Hash(), i)
  13. receipt, _, err := ApplyTransaction(p.config, p.bc, author:nil, gp, statedb, header, tx, totalUsedGas, cfg)
  14. if err != nil { return nil, nil, nil, err}
  15. receipts = append(receipts, receipt)
  16. allLogs = append(allLogs, receipt.Logs...)
  17. }
  18. p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts)
  19. return receipts, allLogs, totalUsedGas, nil
  20. }

GasPool 型別其實就是big.Int。在一個Block的處理過程(即其所有tx的執行過程)中,GasPool 的值能夠告訴你,剩下還有多少Gas可以使用。在每一個tx執行過程中,Ethereum 還設計了償退(refund)環節,所償退的Gas數量也會加到這個GasPool裡。

Process()函式的核心是一個for迴圈,它將Block裡的所有tx逐個遍歷執行。具體的執行函式叫ApplyTransaction(),它每次執行tx, 會返回一個收據(Receipt)物件。Receipt結構體的宣告如下:


Receipt 中有一個Log型別的陣列,其中每一個Log物件記錄了Tx中一小步的操作。所以,每一個tx的執行結果一個Receipt物件來表示;更詳細的內容,由一組Log物件來記錄。這個Log陣列很重要,比如在不同Ethereum節點(Node)的相互同步過程中,待同步區塊的Log陣列有助於驗證同步中收到的block是否正確和完整,所以會被單獨同步(傳輸)。

Receipt的PostState儲存了建立該Receipt物件時,整個Block內所有“帳戶”的當時狀態。Ethereum 裡用stateObject來表示一個賬戶Account,這個賬戶可轉帳(transfer value), 可執行tx, 它的唯一標示符是一個Address型別變數。 這個Receipt.PostState 就是當時所在Block裡所有stateObject物件的RLP Hash值。

Bloom型別是一個Ethereum內部實現的一個256bit長Bloom Filter。 Bloom Filter概念定義可見wikipedia,它可用來快速驗證一個新收到的物件是否處於一個已知的大量物件集合之中。這裡Receipt的Bloom,被用以驗證某個給定的Log是否處於Receipt已有的Log陣列中。

2.1.2 消耗Gas,亦獎勵Gas

我們來看下StateProcessor.ApplyTransaction()的具體實現,它的基本流程如下圖:


ApplyTransaction()首先根據輸入引數分別封裝出一個Message物件和一個EVM物件,然後加上一個傳入的GasPool型別變數,由TransitionDb()函式完成tx的執行,待TransitionDb()返回之後,建立一個收據Receipt物件,最後返回該Recetip物件,以及整個tx執行過程所消耗Gas數量。

GasPool物件是在一個Block執行開始時建立,並在該Block內所有tx的執行過程中共享,對於一個tx的執行可視為“全域性”儲存物件; Message由此次待執行的tx物件轉化而來,並攜帶了解析出的tx的(轉帳)轉出方地址,屬於待處理的資料物件;EVM 作為Ethereum世界裡的虛擬機器(Virtual Machine),作為此次tx的實際執行者,完成轉帳和合約(Contract)的相關操作。

我們來細看下TransitioinDb()的執行過程(/core/state_transition.go)。假設有StateTransition物件st,  其成員變數initialGas表示初始可用Gas數量,gas表示即時可用Gas數量,初始值均為0,於是st.TransitionDb() 可由以下步驟展開:

  1. 購買Gas。首先從交易的(轉帳)轉出方賬戶扣除一筆Ether,費用等於tx.data.GasLimit * tx.data.Price;同時 st.initialGas = st.gas = tx.data.GasLimit;然後(GasPool) gp -= st.gas。
  2. 計算tx的固有Gas消耗 - intrinsicGas。它分為兩個部分,每一個tx預設的消耗量,這個消耗量還因tx是否含有(轉帳)轉入方地址而略有不同;以及針對tx.data.Payload的Gas消耗,Payload型別是[]byte,關於它的固有消耗依賴於[]byte中非0位元組和0位元組的長度。最終,st.gas -= intrinsicGas
  3. EVM執行。如果交易的(轉帳)轉入方地址(tx.data.Recipient)為空,呼叫EVM的Create()函式;否則,呼叫Call()函式。無論哪個函式返回後,更新st.gas。
  4. 計算本次執行交易的實際Gas消耗: requiredGas = st.initialGas - st.gas
  5. 償退Gas。它包括兩個部分:首先將剩餘st.gas 折算成Ether,歸還給交易的(轉帳)轉出方賬戶;然後,基於實際消耗量requiredGas,系統提供一定的補償,數量為refundGas。refundGas 所折算的Ether會被立即加在(轉帳)轉出方賬戶上,同時st.gas += refundGas,gp += st.gas,即剩餘的Gas加上系統補償的Gas,被一起歸併進GasPool,供之後的交易執行使用。
  6. 獎勵所屬區塊的挖掘者:系統給所屬區塊的作者,亦即挖掘者賬戶,增加一筆金額,數額等於 st.data,Price * (st.initialGas - st.gas)。注意,這裡的st.gas在步驟5中被加上了refundGas, 所以這筆獎勵金所對應的Gas,其數量小於該交易實際消耗量requiredGas。

由上可見,除了步驟3中EVM 函式的執行,其他每個步驟都在圍繞著Gas消耗量作文章(EVM 虛擬機器的執行原理容後再述)。到這裡,大家可以對Gas在以太坊系統裡的作用有個初步概念,Gas就是Ethereum系統中的血液。

步驟5的償退機制很有意思,設立它的目的何在?目前為止我只能理解它可以避免交易執行過程中過快消耗Gas,至於對其全面準確的理解尚需時日。

步驟6就更有趣了,正是這個獎勵機制的存在才會吸引社會上的礦工(miner)去賣力“挖礦”(mining)。越大的運算能力帶來越多的的區塊(交易)產出,礦工也就能通過該獎勵機制賺取越多的以太幣。

2.1.3 交易的數字簽名

Ethereum 中每個交易(transaction,tx)物件在被放進block時,都是經過數字簽名的,這樣可以在後續傳輸和處理中隨時驗證tx是否經過篡改。Ethereum 採用的數字簽名是橢圓曲線數字簽名演算法(Elliptic Cure Digital Signature Algorithm,ECDSA)。ECDSA 相比於基於大質數分解的RSA數字簽名演算法,可以在提供相同安全級別(in bits)的同時,僅需更短的公鑰(public key)。關於ECDSA的演算法理論和實現細節,本系列會有另外一篇文章專門加以介紹。這裡需要特別留意的是,tx的轉帳轉出方地址,就是對該tx物件作ECDSA簽名計算時所用的公鑰publicKey

Ethereum中的數字簽名計算過程所生成的簽名(signature), 是一個長度為65bytes的位元組陣列,它被截成三段放進tx中,前32bytes賦值給成員變數R, 再32bytes賦值給S,末1byte賦給V,當然由於R、S、V宣告的型別都是*big.Int, 上述賦值存在[]byte -> big.Int的型別轉換。


當需要恢復出tx物件的轉帳轉出方地址時(比如在需要執行該交易時),Ethereum 會先從tx的signature中恢復出公鑰,再將公鑰轉化成一個common.Address型別的地址,signature由tx物件的三個成員變數R,S,V轉化成位元組陣列[]byte後拼接得到。

Ethereum 對此定義了一個介面Signer, 用來執行掛載簽名,恢復公鑰,對tx物件做雜湊等操作。

  1. // core/types/transaction_signing.go
  2. type Signer innterface {
  3.     Sender(tx *Transaction) (common.Address, error)
  4.     SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error)
  5.     Hash(tx *Transaction) common.Hash
  6.     Equal(Signer) bool
  7. }

生成數字簽名的函式叫SignTx(),它會先呼叫其他函式生成signature, 然後呼叫tx.WithSignature()將signature分段賦值給tx的成員變數R,S,V。

funcSignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey)(*Transaction, error)

恢復出轉出方地址的函式叫Sender(), 引數包括一個Signer, 一個Transaction,程式碼如下:

  1. funcSender(signer Signer, tx *Transaction)(common.Address, error) {
  2. if sc := tx.from().Load(); sc != null {
  3. sigCache := sc.(sigCache)// cache exists,
  4. if sigCache.signer.Equal(signer) {
  5. return sigCache.from, nil
  6. }
  7. }
  8. addr, err := signer.Sender(tx)
  9.     if err != nil {
  10.         return common.Address{}, err
  11.     }
  12. tx.from.Store(sigCache{signer: signer, from: addr}) // cache it
  13. return addr, nil
  14. }
Sender()函式體中,signer.Sender()會從本次數字簽名的簽名字串(signature)中恢復出公鑰,並轉化為tx的(轉帳)轉出方地址。

在上文提到的ApplyTransaction()實現中,Transaction物件需要首先被轉化成Message介面,用到的AsMessage()函式即呼叫了此處的Sender()。

  1. // core/types/transaction.go
  2. func(tx *Transaction)AsMessage(s Signer)(Message,error) {
  3. msg := Message{
  4. price: new(big.Int).Set(tx.data.price)
  5. gasLimit: new(big.Int).Set(tx.data.GasLimit)
  6. ...
  7. }
  8. var err error
  9. msg.from, err = Sender(s, tx)
  10. return msg, err
  11. }

在Transaction物件tx的轉帳轉出方地址被解析出以後,tx 就被完全轉換成了Message型別,可以提供給虛擬機器EVM執行了。

2.2 虛擬機器內

每個交易(Transaction)帶有兩部分內容需要執行:1. 轉帳,由轉出方地址向轉入方地址轉帳一筆以太幣Ether; 2. 攜帶的[]byte型別成員變數Payload,其每一個byte都對應了一個單獨虛擬機器指令。這些內容都是由EVM(Ethereum Virtual Machine)物件來完成的。EVM 結構體是Ethereum虛擬機器機制的核心,它與協同類的UML關係圖如下:


其中Context結構體分別攜帶了Transaction的資訊(GasPrice, GasLimit),Block的資訊(Number, Difficulty),以及轉帳函式等,提供給EVM;StateDB 介面是針對state.StateDB 結構體設計的本地行為介面,可為EVM提供statedb的相關操作; Interpreter結構體作為直譯器,用來解釋執行EVM中合約(Contract)的指令(Code)。

注意,EVM 中定義的成員變數Context和StateDB, 僅僅聲明瞭變數名而無型別,而變數名同時又是其型別名,在Golang中,這種方式意味著宗主結構體可以直接呼叫該成員變數的所有方法和成員變數,比如EVM呼叫Context中的Transfer()。

2.2.1 完成轉帳

交易的轉帳操作由Context物件中的TransferFunc型別函式來實現,類似的函式型別,還有CanTransferFunc, 和GetHashFunc。

  1. // core/vm/evm.go
  2. type {
  3.     CanTransferFunc func(StateDB, common.Address, *big.Int)
  4.     TransferFunc func(StateDB, common.Address, common.Address, *big.Int)
  5.     GetHashFunc func(uint64) common.Hash
  6. }

這三個型別的函式變數CanTransfer, Transfer, GetHash,在Context初始化時從外部傳入,目前使用的均是一個本地實現:

  1. // core/evm.go
  2. funcNewEVMContext(msg Message, header *Header, chain ChainContext, author *Address){
  3.     return vm.Context {
  4.         CanTransfer: CanTransfer,
  5.         Transfer: Transfer,
  6.         GetHash: GetHash(header, chain),
  7.         ...
  8.     }
  9. }
  10. funcCanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) {
  11.     return db.GetBalance(addr).Cmp(amount) >= 0
  12. }
  13. funcTransfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
  14. db.SubBalance(sender, amount)
  15. db.AddBalance(recipient, amount)
  16. }
可見目前的轉帳函式Transfer()的邏輯非常簡單,轉帳的轉出賬戶減掉一筆以太幣,轉入賬戶加上一筆以太幣。由於EVM呼叫的Transfer()函式實現完全由Context提供,所以,假設如果基於Ethereum平臺開發,需要設計一種全新的“轉帳”模式,那麼只需寫一個新的Transfer()函式實現,在Context初始化時賦值即可。

有朋友或許會問,這裡Transfer()函式中對轉出和轉入賬戶的操作會立即生效麼?萬一兩步操作之間有錯誤發生怎麼辦?答案是不會立即生效。StateDB 並不是真正的資料庫,只是一行為類似資料庫的結構體。它在內部以Trie的資料結構來管理各個基於地址的賬戶,可以理解成一個cache;當該賬戶的資訊有變化時,變化先儲存在Trie中。僅當整個Block要被插入到BlockChain時,StateDB 裡快取的所有賬戶的所有改動,才會被真正的提交到底層資料庫。

2.2.2 合約的建立和賦值

合約(Contract)是EVM用來執行(虛擬機器)指令的結構體。先來看下Contract的定義:

  1. // core/vm/contract.go
  2. type ContractRef interface {
  3.     Address() common.Address
  4. }
  5. type Contract struct {
  6. CallerAddress common.Address
  7. caller ContractRef
  8. self ContractRef
  9. jumpdests destinations
  10. Code []byte
  11. CodeHash common.Hash
  12. CodeAddr *Address
  13. Input []byte
  14. Gas uint64
  15. value *big.Int
  16. Args []byte
  17. DelegateCall

    相關推薦

    原始碼I.區塊交易合約虛擬機器

    最近在看以太坊(Ethereum)的原始碼, 初初看出點眉目。 區塊鏈是近年熱點之一,面向大眾讀者介紹概念的文章無數,有興趣的朋友可自行搜尋。我會從原始碼實現入手,較系統的介紹一下以太坊的系統設計和協議實現等,希望能提供有一定深度的內容,歡迎有興趣的朋友多多討論。注:1.原始

    開發發幣指南--進階篇

    參考:https://www.ethereum.org/token pragma solidity ^0.4.16; interface tokenRecipient { function receiveApproval(address _from, uint256 _value,

    開發如何開發一個編譯智能合約並且發布的平臺(二)

    vid new 有一個 tran slim ole https parse 交易 接上一章的內容,這篇介紹 deploy相關和結果演示。 deploy一個合約的過程中,需要計算發布的消耗和nonce值。 當進行每筆交易時,發送人設定Gas Limit 和Gas Pric

    開發利用Oraclize開發一個投注合約(一):原理介紹

    智慧合約的作用很多,但是很多資料還是要基於網際網路,那麼如何在合約中獲取網際網路中的資料?Oraclize就是為了這個目的而誕生的。 本篇介紹如何利用Oraclize開發一個投注智慧合約,開始coding之前,這一節講述一下理論。 工作原理: 智慧合約通過對Oraclize釋出一個合約之間的呼叫請求來獲

    開發利用Oraclize開發一個投註合約(一):原理介紹

    tween 協議 簡單方法 type callback 使用 抓取 獲取 num 智能合約的作用很多,但是很多數據還是要基於互聯網,那麽如何在合約中獲取互聯網中的數據?Oraclize就是為了這個目的而誕生的。 本篇介紹如何利用Oraclize開發一個投註智能合約,開始co

    開發geth的使用入門

    geth的全稱是go-ethereum,是一個以太坊客戶端,用go語言編寫,應該是目前最常用的客戶端。當然以太坊客戶端還有用C++,Ruby,Python,Java等其他多種語言編寫的,不同型別的客戶端是為了滿足不同的需求場景。今天我們主要來介紹geth(發音同guess )

    開發Web3j對錢包功能的實現

    public class App {    // GAS價格      public static BigInteger GAS_PRICE = BigInteger.valueOf(20_000_000_000L);      // GAS上限      public static BigInteger G

    死磕原始碼分析之區塊上鍊入庫

    > 死磕以太坊原始碼分析之區塊上鍊入庫 > > 配合以下程式碼進行閱讀:https://github.com/blockchainGuide/ > > 寫文不易,給個小關注,有什麼問題可以指出,便於大家交流學習。 ## 引言 不管是礦工挖礦還是`Fetcher`同步,`Dow

    26.原始碼分析(26)core-txpool交易原始碼分析

    txpool主要用來存放當前提交的等待寫入區塊的交易,有遠端和本地的。 txpool裡面的交易分為兩種, 提交但是還不能執行的,放在queue裡面等待能夠執行(比如說nonce太高)。 等待執行的,放在pending裡面等待執行。 從txpool的測試案例來看

    25.原始碼分析(25)core-txlist交易池的一些資料結構原始碼分析

    nonceHeap nonceHeap實現了一個heap.Interface的資料結構,用來實現了一個堆的資料結構。 在heap.Interface的文件介紹中,預設實現的是最小堆。 如果h是一個數組,只要陣列中的資料滿足下面的要求。那麼就認為h是一個最小堆。 !h.Less(j

    我的區塊鏈之路- 原始碼剖析之Geth節點啟動全量過程詳解

    最近在整理前端時間學習的原始碼,由於原始碼的學習是片段的,那麼我們在這篇文章中把它關聯起來,這篇文章我們講P2P部分,我們會從Geth的入口一直到後面的節點發現,節點間廣播及同步TX和Block的講解。首先,我這裡先不說fetcher 及downloader的具體工作流程

    我的區塊鏈之路- 原始碼剖析之Geth 1.8.14版本挖礦邏輯調整

    今天為什麼寫這個文章呢,首先,前段時間有朋友問過我,說現在geth的1.8.14版本的程式碼和網上各路大神們的分析不一樣了。我就趕緊看了下,確實,親的geth程式碼中的mine部分的邏輯有所改動,想必看過原始碼的都知道,之前的miner真正挖礦是由worker把所需挖礦的

    Go學習之go-ethereum原始碼分析(一)

    關於Go語言環境的安裝與配置,我在《入門篇》進行了詳細講解,有需要的朋友可以前往閱讀,本文進入當下比較火熱的區塊鏈專案 - 以太坊(go-ethereum)進行原始碼解讀。本文內容純屬個人見解,有錯誤理解或者不足之處還請見諒,歡迎一起交流學習。    - 環境準備    -

    系列-004原始碼搭建etherum,solidity編譯環境

    使用ubuntu1804(由ubuntu-18.04.1-server-amd64.iso安裝)環境 說明:  本文主要介紹基於ubuntu1804的環境,由原始碼構建etherum,以及solidity的編譯環境。 1, 安裝go mkdir -p /home/001_

    區塊鏈入門教程原始碼分析交易資料分析eth

    交易的資料結構 交易的資料結構定義在core.types.transaction.go中,結構如下: type Transaction struct { data txdata // caches hash atomic.Value size atomic.Value from atomic.Value

    原始碼解讀(6)blockchain區塊插入校驗分析

    以太坊blockchain的管理事務: 1、blockchain模組初始化 2、blockchain模組插入校驗分析 3、blockchain模組區塊鏈分叉處理 4、blockchian模組規範鏈更新 上一節分析了blockchain的初始化,這一節來分析blockchain區塊的插入和校驗

    兄弟連區塊鏈教程原始碼分析CMD深入分析(一)

    cmd包分析 cmd下面總共有13個子包,除了util包之外,每個子包都有一個主函式,每個主函式的init方法中都定義了該主函式支援的命令,如 geth包下面的: func init() {     // Initialize the CLI app and st

    22.原始碼分析(22)core-genesis創世區塊原始碼分析

    genesis 是創世區塊的意思. 一個區塊鏈就是從同一個創世區塊開始,通過規則形成的.不同的網路有不同的創世區塊, 主網路和測試網路的創世區塊是不同的. 這個模組根據傳入的genesis的初始值和database,來設定genesis的狀態,如果不存在創世區塊,那麼在database裡

    錢包開發 二MyEtherWallet 錢包介紹

    以太坊常見錢包包括:Ethereum Wallet、MyEtherWallet、MetaMask、Parity。咱們的錢包開發專案主要圍繞MyEtherWallet錢包的相關功能進行開發,因此下面主要介紹MyEtherWallet的常用功能。 MyEtherWallet 是一個輕錢包,使用起來最簡單,無需下

    區塊鏈100講:原始碼研究之PoW及共識演算法深究

    本講將介紹“挖礦“得到新區塊的整個過程,以及不同共識演算法的實現細節。 1 待挖掘區塊需要組裝 在Ethereum 程式碼中,名為miner的包(package)負責向外提供一個“挖礦”得到的新區塊,其主要結構體的UML關係圖如下圖所示: