比特币價格的(de)上上下(xià)下(xià),始終撩動著(zhe)每一個(gè)人(rén)無比關切的(de)小心髒。從去年初的(de) 800 美(měi)元左右,飛(fēi)漲到去年底到 19783.21 美(měi)元最高(gāo)點,不到1年,便有将近 25 倍的(de)升值速度。盡管眼下(xià)又掉回 8000 多(duō)美(měi)元的(de)價格,但價格差不多(duō)能搞出去年同期一個(gè)數量級,币圈人(rén)士“過去一年比以往 10 年掙的(de)都多(duō)”,已經是不争的(de)事實。
而對(duì)區(qū)塊鏈開發者來(lái)說,據說也(yě)已經有拿到年新 500 萬的(de)天價。所以“跑步進入區(qū)塊鏈”,已經成爲不少程序員(yuán)的(de)共識。但是看過很多(duō)遠(yuǎn)離,我們如何才能迅速上手呢(ne)?國外網友 Ken Shirriff 在博客中分(fēn)享了(le)他(tā)在手動茶古劍比特币交易時(shí)的(de)代碼與對(duì)比特币協議(yì)的(de)心得(de),區(qū)塊鏈大(dà)本營編譯如下(xià)。
近期,媒體行業對(duì)比特币表現出極大(dà)的(de)熱(rè)情,這(zhè)鼓舞著(zhe)我從網絡底層的(de)數據流開始,認真學習(xí)比特币的(de)工作原理(lǐ)。通(tōng)常人(rén)們會使用(yòng)錢包軟件來(lái)進行比特币交易,錢包軟件在方便用(yòng)戶的(de)同時(shí),向用(yòng)戶隐藏了(le)比特币的(de)交易流程,而我想親自動手來(lái)體驗比特币交易,我的(de)目标是用(yòng)Python手動創建一筆比特币交易,以十六進制數據的(de)形式将交易廣播到比特币網絡中,然後觀察這(zhè)筆交易是怎麽被加入到區(qū)塊鏈中的(de)。事實證明(míng),這(zhè)個(gè)過程很有趣,希望你也(yě)對(duì)它感興趣。
在本篇文章(zhāng)中,首先我會對(duì)比特币進行一個(gè)簡單的(de)概述,之後,我會從以下(xià)幾個(gè)方面帶領你們學習(xí)比特币:創建一個(gè)比特币地址(比特币中的(de)賬戶),進行一筆比特币交易,簽署交易,将交易廣播到比特币網絡中,最後等待交易的(de)确認。
比特币簡述:
首先,我會介紹一下(xià)比特币系統是怎麽運轉的(de),然後再深入探討(tǎo)整個(gè)細節。比特币是一個(gè)基于點對(duì)點網絡的(de)電子貨币,你可(kě)以用(yòng)現金在網上購(gòu)買比特币,用(yòng)比特币向他(tā)人(rén)轉賬,在有些商家,你可(kě)以像使用(yòng)支付寶一樣使用(yòng)比特币付款,當然,你也(yě)可(kě)以賣出所持有的(de)比特币換回現金。
簡而言之,在比特币網絡中,分(fēn)布式賬本(區(qū)塊鏈)記錄并随時(shí)更新著(zhe)每個(gè)比特币的(de)所有權。與銀行不同的(de)是,比特币并沒有與個(gè)人(rén)或個(gè)人(rén)的(de)賬戶綁定,相反的(de),比特币隻屬于一個(gè)個(gè)比特币地址,比如:1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa。這(zhè)裏你可(kě)能已經繞暈了(le),難道這(zhè)段字符中藏著(zhe)比特币?當然不是,比特币地址是比特币網絡中的(de)一個(gè)身份,也(yě)可(kě)以通(tōng)俗地說是你在比特币中開的(de)一個(gè)“銀行賬戶”,我們用(yòng)這(zhè)個(gè)“賬戶”來(lái)進行交易。在網站:blockchain.info中,你可(kě)以查到所有的(de)交易信息:
比特币賬戶信息
但是怎麽證明(míng)這(zhè)個(gè)賬戶是我的(de)呢(ne),不急,先往下(xià)看,你的(de)疑問我會爲你一一解答(dá)。
比特币交易
如何像使用(yòng)現金一樣使用(yòng)比特币呢(ne)?答(dá)案是創建一筆交易。在一筆交易中,比特币的(de)所有者(上文提到過比特币的(de)所有者是比特币地址)将所有權轉移到一個(gè)新的(de)比特币地址。比特币的(de)一個(gè)颠覆性創新就是通(tōng)過鼓勵節點記賬(也(yě)叫礦工挖礦),将交易記錄放在一個(gè)分(fēn)布式的(de)數據庫中。交易被集合在區(qū)塊中,大(dà)概每十分(fēn)鐘(zhōng)比特币網絡中産生一個(gè)新的(de)區(qū)塊,成爲交易記錄的(de)一部分(fēn),稱爲區(qū)塊鏈。加入到區(qū)塊鏈中的(de)交易可(kě)以被認爲是一筆成功的(de)交易。現在問題來(lái)了(le),誰來(lái)給你記賬呢(ne)?是礦工,礦工的(de)挖礦過程就是在往區(qū)塊鏈中記賬,礦工要核實每筆交易是否正确,核實完後,礦工們就開始算(suàn)一道很難的(de)數學題(密碼學中的(de)哈希函數),最早算(suàn)出答(dá)案的(de)人(rén)就能生成一個(gè)區(qū)塊,也(yě)叫挖出了(le)一個(gè)新的(de)區(qū)塊,這(zhè)個(gè)區(qū)塊将成爲區(qū)塊鏈的(de)新一部分(fēn)。
也(yě)許你會問了(le),明(míng)明(míng)是記賬,幹著(zhe)會計的(de)活,爲什(shén)麽要叫挖礦呢(ne)?和(hé)傳統的(de)在地下(xià)挖礦石一樣,比特币挖礦也(yě)是會有收獲的(de)。挖礦是一種新發行比特币的(de)過程,當前,每挖到一個(gè)礦,礦工會得(de)到系統獎勵的(de)12.5個(gè)比特币,按目前一個(gè)比特币接近一萬美(měi)元的(de)市價,這(zhè)就是一筆12.5萬美(měi)元的(de)巨款。此外,礦工還(hái)可(kě)以獲得(de)本區(qū)塊中所有的(de)交易費,舉例來(lái)說,在高(gāo)度爲512587的(de)區(qū)塊中,幸運的(de)礦工總共收獲了(le)12.829個(gè)比特币。正因如此,礦工之間的(de)競争十分(fēn)激烈,采礦的(de)難度與礦工間激烈的(de)競争是比特币安全的(de)重要保證,因爲這(zhè)樣可(kě)以保證沒有壞人(rén)能操縱系統。
點對(duì)點網絡
比特币并沒有一個(gè)中央服務器,相反,比特币在一個(gè)點對(duì)點網絡中運行。如果你運行一個(gè)比特币節點,那你就成了(le)網絡的(de)一部分(fēn)。比特币網絡中的(de)節點彼此交換自己存儲的(de)交易,區(qū)塊,以及IP地址信息(用(yòng)于節點間建立連接互相通(tōng)信)。當你第一次連接到比特币網絡,你的(de)節點會從随機挑選的(de)節點中下(xià)載區(qū)塊鏈的(de)信息。反過來(lái),你的(de)節點也(yě)會向後加入者提供信息。當你要創建一筆比特币交易時(shí),你要把這(zhè)筆交易發送給一些節點,這(zhè)些節點會在比特币網絡中廣播這(zhè)筆交易,直到全網都收到這(zhè)筆交易。礦工們會收集你的(de)交易信息,生成一個(gè)含有你這(zhè)筆交易的(de)區(qū)塊,向全網廣播,這(zhè)時(shí),你的(de)節點也(yě)會收到這(zhè)個(gè)區(qū)塊信息,通(tōng)過驗證,這(zhè)筆交易被加入到了(le)區(qū)塊鏈中,你就交易成功了(le)。
加密技術
現在回到證明(míng)比特币賬戶是誰的(de)這(zhè)個(gè)問題。比特币使用(yòng)數字簽名技術以确保隻有比特币賬戶的(de)所有者才能使用(yòng)賬戶中的(de)比特币。比特币地址的(de)所有者擁有與該地址相匹配的(de)私鑰,當花費比特币時(shí),你用(yòng)要這(zhè)個(gè)私鑰在交易上簽名,證明(míng)自己是這(zhè)個(gè)賬戶的(de)所有者。這(zhè)有點像現實生活中的(de)蓋章(zhāng),蓋章(zhāng)就意味著(zhe)授權。怎麽驗證呢(ne),公鑰與比特币賬戶相關聯,用(yòng)公鑰就可(kě)以驗證簽名是否正确。這(zhè)樣就解決了(le)比特币賬戶是誰的(de)這(zhè)個(gè)問題。
怎麽來(lái)區(qū)分(fēn)不同的(de)交易呢(ne)?交易和(hé)區(qū)塊都使用(yòng)密碼學上的(de)哈希值進行索引,是不是有點耳熟,對(duì),在比特币協議(yì)中,多(duō)處使用(yòng)到了(le)哈希函數,礦工們剛才算(suàn)的(de)數學題就是在算(suàn)哈希函數。
比特币協議(yì)探究
在接下(xià)來(lái)的(de)文章(zhāng)裏,我将逐步介紹我是怎樣手動進行一次比特币交易的(de)。首先,我生成了(le)一個(gè)比特币賬戶以及對(duì)應的(de)公鑰,私鑰。接下(xià)來(lái)我發起了(le)一筆比特币交易,我向這(zhè)個(gè)新生成的(de)賬戶轉了(le)一小筆比特币。期間手動簽署這(zhè)筆交易很困難,它花費了(le)我很多(duō)的(de)時(shí)間。最後,我将這(zhè)筆交易發送到比特币網絡,等待它被加入區(qū)塊鏈。本文的(de)其餘部分(fēn)會詳細地介紹這(zhè)些步驟。
事實證明(míng),手動進行比特币交易比我想象中的(de)更加困難。正如你所看到的(de),比特币的(de)協議(yì)有些許混亂:它使用(yòng)了(le)大(dà)端格式數字(高(gāo)位編址,将高(gāo)序字節存儲在起始地址),小端格式數字(低位編址,将低序字節存儲在起始位置),固定長(cháng)度數字,可(kě)變長(cháng)度數字,自定義編碼格式,DER編碼格式以及各種加密算(suàn)法。因此,僅僅是将數據轉換爲正确的(de)格式就浪費了(le)很多(duō)時(shí)間。
我遇到的(de)第二個(gè)難題就是加密,嘗試一下(xià)手動加密,你就會發現密碼學對(duì)人(rén)們多(duō)不友好,甚至可(kě)以說是無情。即使你隻輸錯了(le)一個(gè)字節,交易就會因出錯被拒絕,而且它不會告訴你哪裏出錯了(le),你隻能重來(lái)。
最後,手動簽署交易的(de)過程也(yě)比想象中難得(de)多(duō),簽署交易時(shí)每個(gè)環節都必須零失誤,要麽又要退回重來(lái)。
比特币地址和(hé)密鑰
第一步,我創建了(le)一個(gè)比特币地址。通(tōng)常情況下(xià),人(rén)們都是使用(yòng)比特币客戶端軟件來(lái)創建比特币地址和(hé)與之相關的(de)密鑰。本著(zhe)學習(xí)的(de)态度,我寫了(le)一些Python代碼來(lái)生成比特币地址,從而揭示地址創建的(de)機理(lǐ)。
比特币使用(yòng)了(le)一系列的(de)密鑰和(hé)地址,下(xià)圖解釋了(le)它們的(de)關系。首先你要創建一個(gè)随機的(de)256位的(de)私鑰,這(zhè)個(gè)私鑰用(yòng)于在花費比特币時(shí)簽署交易。因此,私鑰必須保密,否則你的(de)比特币可(kě)能會被盜用(yòng)。
橢圓曲線數字簽名算(suàn)法(Elliptic Curve Digital Signature Algorithm,ECDSA,美(měi)國政府的(de)标準,接下(xià)來(lái)我們會討(tǎo)論它)會從私鑰中生成一個(gè)512位的(de)公鑰,這(zhè)個(gè)公鑰用(yòng)于驗證交易的(de)簽名。但不方便的(de)是,比特币協議(yì)中需要在這(zhè)個(gè)公鑰上添加了(le)前綴04,這(zhè)個(gè)公鑰在交易簽署之前不會被洩露,不像其它系統中公鑰就是爲了(le)公之于衆的(de)。
比特币地址與公鑰的(de)關系
下(xià)一步就是生成與他(tā)人(rén)交易時(shí)使用(yòng)的(de)比特币地址了(le)。512位的(de)公鑰太長(cháng)不方便使用(yòng),因此使用(yòng)SHA-256和(hé)RIPEMD哈希算(suàn)法将其縮小爲160位。然後使用(yòng)比特币定義的(de)Base58Check 編碼将密鑰編碼爲ASCII(American Standard Code for Information Interchange,美(měi)國信息交換标準代碼)格式。得(de)到的(de)地址(例如上文中的(de):1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa)就是你接收别人(rén)比特币時(shí)要發布的(de)地址。需要注意的(de)是,你無法從比特币地址中複原出公鑰或私鑰。如果你丢失了(le)你的(de)私鑰(比如說你把私鑰存在你的(de)硬盤上,但硬盤丢失),你的(de)比特币将永遠(yuǎn)丢失。
最後,錢包交換格式密鑰(WIF)用(yòng)于将私鑰添加到你的(de)錢包軟件中,這(zhè)隻是将私鑰進行Base58Check編碼轉換爲ASCII格式,這(zhè)一步是可(kě)逆的(de),而且很容易經過逆變換恢複出256位的(de)私鑰。(圖中有我的(de)私鑰,我很好奇是否有人(rén)會用(yòng)我的(de)私鑰去偷(通(tōng)過私鑰簽署交易,從而轉走)我那價值80美(měi)分(fēn)的(de)比特币,然而真有人(rén)那麽做(zuò)了(le),可(kě)以在
https://blockchain.info/address/1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa 看到,也(yě)算(suàn)是爲教學做(zuò)貢獻了(le)。)
總之,共有三種密鑰:私鑰,公鑰,公鑰的(de)哈希值,經過使用(yòng)Base58Check編碼,它們對(duì)外都是以ASCII格式表示。私鑰是其中最重要的(de)密鑰,因爲花費比特币時(shí)需要私鑰簽署交易,而且其他(tā)的(de)密鑰都可(kě)以從私鑰中産生。公鑰的(de)哈希值就是你們剛看的(de)的(de)比特币地址。
我使用(yòng)下(xià)面的(de)代碼片段來(lái)生成WIF格式的(de)私鑰和(hé)地址。私鑰隻是一個(gè)随機的(de)256位的(de)數字,使用(yòng)橢圓曲線數字簽名算(suàn)法從私鑰中生成公鑰,公鑰使用(yòng)SHA-256算(suàn)法,RIPEMD-160算(suàn)法進行哈希計算(suàn),再經Base58編碼并進行校驗後得(de)到比特币地址。最後,私鑰用(yòng)Base58Check編碼以生成用(yòng)于将私鑰輸入錢包軟件的(de)WIF編碼。注意,這(zhè)段Python随機函數代碼在密碼學上安全性并不高(gāo),如果你想要嘗試這(zhè)一步驟,建議(yì)使用(yòng)更安全的(de)錢包軟件來(lái)生成比特币地址和(hé)密鑰。
def privateKeyToWif(key_hex): return utils.base58CheckEncode(0x80, key_hex.decode('hex')) def privateKeyToPublicKey(s): sk = ecdsa.SigningKey.from_string(s.decode('hex'), curve=ecdsa.SECP256k1) vk = sk.verifying_key
return ('\04' + sk.verifying_key.to_string()).encode('hex')
def pubKeyToAddr(s): ripemd160 = hashlib.new('ripemd160') ripemd160.update(hashlib.sha256(s.decode('hex')).digest())
return utils.base58CheckEncode(0, ripemd160.digest())
def keyToAddr(s):
return pubKeyToAddr(privateKeyToPublicKey(s))
# Warning: this random function is not cryptographically strong and is just for example
private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)]) print keyUtils.privateKeyToWif(private_key) print keyUtils.keyToAddr(private_key)
keyUtils.py
從内部分(fēn)析一筆交易
交易是比特币系統的(de)基本操作,也(yě)許你會認爲交易就是簡單地把比特币從一個(gè)地址轉移到另一個(gè)地址,但交易其實并不簡單。一筆交易包含一個(gè)或多(duō)個(gè)輸入和(hé)輸出,交易中的(de)每個(gè)輸入的(de)地址都提供比特币,每個(gè)輸出的(de)地址都接受比特币。
一筆簡單的(de)比特币交易,交易C花費了(le)從交易A和(hé)交易B獲得(de)的(de)0.008個(gè)比特币,其中0.001個(gè)比特币被當作交易費付給礦工
上圖顯示了(le)一筆簡單的(de)比特币交易“C”,在這(zhè)筆交易中,有0.005個(gè)比特币是在交易A中獲得(de)的(de),0.003個(gè)比特币是在交易B中獲得(de)的(de)。(圖中箭頭是由新交易的(de)輸入指向得(de)到這(zhè)些比特币的(de)交易的(de)輸出,所以比特币的(de)流向是逆著(zhe)箭頭方向的(de)。)對(duì)于輸出,有0.003個(gè)比特币給了(le)第一個(gè)比特币地址,有0.004個(gè)比特币給了(le)第二個(gè)比特币地址,剩餘的(de)0.001個(gè)比特币作爲交易費付給礦工。請注意,本次交易并沒有影(yǐng)響到在交易A中另一個(gè)輸出爲0.015的(de)比特币。
在一筆交易中,輸入的(de)比特币地址必須花出所有的(de)比特币,假如你在之前的(de)交易收到了(le)100個(gè)比特币,但你隻想花1個(gè)比特币,創建這(zhè)筆交易你必須花完所有的(de)100個(gè)比特币,那剩下(xià)的(de)99個(gè)比特币怎麽辦呢(ne)?解決方案就是在交易中再增加一個(gè)輸出,将剩餘的(de)99個(gè)比特币轉給自己。這(zhè)樣你就可(kě)以花費任意數額的(de)比特币。
通(tōng)常交易要支付交易費,如果一筆交易中輸入的(de)比特币總和(hé)大(dà)于輸出的(de)比特币的(de)總和(hé),剩餘的(de)費用(yòng)就是給礦工的(de)交易費。這(zhè)筆費用(yòng)并沒有明(míng)确要求,但是對(duì)于礦工而言,沒有交易費的(de)交易就會被列爲低優先級交易,可(kě)能要等上幾天才會被處理(lǐ)甚至被礦工直接丢棄。交易費通(tōng)常并不高(gāo),但它可(kě)能影(yǐng)響著(zhe)你的(de)交易。
手動創建一筆交易
如下(xià)圖所示,在我的(de)實驗中我發起了(le)一筆隻有一個(gè)輸入一個(gè)輸出的(de)交易。我在Coinbase上買了(le)一些比特币,并将0.00101234個(gè)比特币放入地址:
1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5中,這(zhè)筆交易哈希爲:
81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48,我的(de)目标是創建一筆交易,将這(zhè)些比特币轉入我的(de)另一個(gè)地址:
1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa,扣除0.0001個(gè)比特币的(de)交易費後,目标地址将獲得(de)0.00091234個(gè)比特币。
比特币交易結構實例
Blockchain.info上的(de)交易記錄
https://blockchain.info/address/1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5?filter=4
按照(zhào)協議(yì)标準,創建這(zhè)筆交易很簡單。如下(xià)表所示,這(zhè)筆交易隻有一個(gè)輸入,源自于81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48中的(de)輸出0(第一個(gè)輸出)。輸出爲0.00091234個(gè)比特币(91234在十六進制中用(yòng)0x016462表示),它以小端格式存儲在值區(qū)域中。加密過程中的(de)scriptSig和(hé)scriptPubKey較爲複雜(zá),我們稍後再做(zuò)討(tǎo)論。
version |
01 00 00 00 |
|
input count |
01 |
|
input |
previous output hash(reversed) |
48 4d 40 d4 5b 9e a0 d6 52 fc a8 25 8a b7 ca a4 25 41 eb 52 97 58 57 f9 6f b5 0c d7 32 c8 b4 81 |
previous output index |
00 00 00 00 |
|
script length |
||
scriptSig |
script containing signature |
|
sequence |
ff ff ff ff |
|
output count |
01 |
|
output |
value |
62 64 01 00 00 00 00 00 |
script length |
||
scriptPubKey |
script containing destination address |
|
block lock time |
00 00 00 00 |
這(zhè)是我生成交易使用(yòng)的(de)代碼,這(zhè)段代碼隻是把數據打包成二進制文件。簽署交易較爲困難,我們等一會兒(ér)再說。
# Makes a transaction from the inputs# outputs is a list of [redemptionSatoshis, outputScript]
def makeRawTransaction(outputTransactionHash, sourceIndex, scriptSig, outputs):
def makeOutput(data): redemptionSatoshis, outputScript = data
return (struct.pack("<Q", redemptionSatoshis).encode('hex') +
'%02x' % len(outputScript.decode('hex')) + outputScript) formattedOutputs = ''.join(map(makeOutput, outputs))
return (
"01000000" + # 4 bytes version "01" + # varint for number of inputs outputTransactionHash.decode('hex')[::-1].encode('hex') + # reverse outputTransactionHash struct.pack('<L', sourceIndex).encode('hex') +
'%02x' % len(scriptSig.decode('hex')) + scriptSig +
"ffffffff" + # sequence "%02x" % len(outputs) + # number of outputs formattedOutputs +
"00000000" # lockTime
)
txnUtils.py
比特币交易怎樣簽署
下(xià)圖爲我們簡單描述了(le)交易是如何簽署并相互連接的(de)。針對(duì)中間這(zhè)筆從比特币地址B轉賬到比特币地址C的(de)交易。交易的(de)内容(包括前一個(gè)交易的(de)哈希值(索引))被進行哈希計算(suàn)并用(yòng)B的(de)私鑰簽名。另外,B的(de)公鑰也(yě)被包含在了(le)交易中。
通(tōng)過執行幾個(gè)簡單運算(suàn),任何人(rén)都能驗證B是否簽署了(le)這(zhè)筆交易。首先,B的(de)公鑰與之前收到這(zhè)筆比特币交易的(de)地址做(zuò)驗證,證明(míng)B的(de)公鑰有效。(正如前面所說的(de),地址很容易從公鑰中計算(suàn)獲得(de))。接下(xià)來(lái),可(kě)以通(tōng)過B的(de)公鑰驗證B交易簽名的(de)真僞。這(zhè)些步驟能确保交易的(de)有效性和(hé)交易得(de)到B的(de)授權。比特币于衆不同的(de)一點是,B的(de)公鑰在B發起交易之前是不公開的(de)。
在比特币系統中,比特币通(tōng)過區(qū)塊鏈上的(de)一筆筆交易在不同的(de)地址間傳遞。區(qū)塊鏈上的(de)每一筆交易都能被驗證以确保比特币交易的(de)有效性。
比特币腳本語言
你可(kě)能會以爲僅僅通(tōng)過在交易内容中附上簽名就可(kě)以簽署比特币交易,其實不然,這(zhè)個(gè)過程十分(fēn)複雜(zá)。實際上,每一筆交易中都包含一個(gè)“小程序”,用(yòng)于确認交易是否有效。這(zhè)個(gè)“小程序”用(yòng)腳本語言寫成,通(tōng)過這(zhè)種基于堆棧的(de)比特币腳本語言,我們可(kě)以應對(duì)許多(duō)複雜(zá)的(de)比特币支付場(chǎng)景。例如,托管系統可(kě)以設定隻要經過三分(fēn)之二的(de)用(yòng)戶授權,就可(kě)執行交易的(de)規則,也(yě)可(kě)以設置其他(tā)的(de)合約。
腳本語言十分(fēn)複雜(zá),大(dà)約有80種操作碼,包括算(suàn)數計算(suàn),按位操作,字符串處理(lǐ),條件語句和(hé)堆棧操作。腳本語言也(yě)包含一些必要的(de)密碼學操作(SHA-256,RIPEMD等等)作爲原語(原語是執行過程中不可(kě)被打斷的(de)基本操作,你可(kě)以理(lǐ)解爲一段代碼)。爲了(le)确保腳本語言可(kě)以運行完畢自動退出,該語言不支持任何循環操作,因此它不是圖靈完備的(de)。然而,實際上,它隻支持少數類型的(de)交易。
前一個(gè)交易中的(de)腳本稱爲scriptPubKey,當前交易中的(de)腳本稱爲scriptSig。要驗證交易時(shí),先執行scriptSig,然後再執行scriptPubKey。如果兩個(gè)腳本都成功執行,交易就被認定爲有效,交易中的(de)比特币就可(kě)以成功花出。否則,交易無效。要注意的(de)是前一個(gè)交易中的(de)scriptPubKey規定了(le)花費比特币的(de)條件,當前交易的(de)scriptSig必須滿足這(zhè)個(gè)條件。
在一個(gè)标準的(de)交易中,scriptSig腳本将從私鑰中生成的(de)簽名并壓入堆棧中,再壓入公鑰。接下(xià)來(lái)scriptPubKey腳本會執行運算(suàn)先驗證公鑰的(de)有效性,再驗證簽名的(de)有效性。
正如腳本中所表示,scriptSig:
PUSHDATA
signature data and SIGHASH_ALL
PUSHDATA
public key data
scriptPubKey:
OP_DUP OP_HASH160 PUSHDATA Bitcoin address (public key hash) OP_EQUALVERIFY OP_CHECKSIG
當這(zhè)段代碼執行時(shí),PUSHDATA操作首先會把簽名壓入堆棧,接著(zhe)把公鑰壓入堆棧。OPHASH-160操作計算(suàn)公鑰的(de)160位哈希值,PUSHDATA操作再把交易中的(de)輸入地址(輸入賬号)壓入堆棧,然後,OP-EQUALVERIFY操作驗證驗證前兩個(gè)堆棧中的(de)值是否相等(驗證這(zhè)筆交易中你使用(yòng)的(de)比特币是否屬于你自己)-如果公鑰的(de)哈希等于之前交易中的(de)輸出地址,這(zhè)就證明(míng)公鑰是有效的(de)(證明(míng)這(zhè)個(gè)比特币是你的(de))。最後,OP_CHECKSIG操作将檢查交易的(de)簽名是否與堆棧裏的(de)公鑰和(hé)簽名匹配,匹配就證明(míng)簽名是有效的(de)(證明(míng)交易的(de)到了(le)你的(de)授權)
簽署交易
我發現簽署這(zhè)筆交易是手動使用(yòng)比特币時(shí)最難的(de)地方,這(zhè)一過程出奇地困難且容易出錯。簽名的(de)基本思想很簡單,使用(yòng)橢圓曲線簽名算(suàn)法和(hé)私鑰來(lái)生成交易的(de)數字簽名,但細節非常棘手。簽署交易的(de)過程可(kě)以通(tōng)過這(zhè)19個(gè)步驟描述。
簽署交易的(de)19個(gè)步驟
對(duì)交易的(de)簽名讓我面臨巨大(dà)的(de)挑戰,這(zhè)涉及到一個(gè)如何在交易内容中還(hái)沒有加入簽名時(shí)簽署這(zhè)筆交易的(de)問題。爲了(le)避免這(zhè)個(gè)問題,在計算(suàn)生成簽名之前,我把scriptPubKey這(zhè)個(gè)腳本從上一筆交易複制到當前交易中(當前這(zhè)筆交易正在被簽署),然後将簽名轉換爲腳本語言的(de)代碼,創建嵌入在當前交易中的(de)scriptSig腳本。對(duì)于具有多(duō)個(gè)輸入的(de)交易,簽署交易環節更加複雜(zá),因爲每個(gè)輸入都需要單獨的(de)簽名,這(zhè)裏我就不做(zuò)詳細討(tǎo)論了(le)。
哈希值這(zhè)一步驟難倒了(le)我。在簽名之前,交易中有一個(gè)臨時(shí)附加的(de)哈希值常量。對(duì)于常規的(de)交易,這(zhè)個(gè)值是SIGHASH_ALL(0x00000001)。簽名後,這(zhè)個(gè)哈希值将從交易内容的(de)最後删除,附加到scriptSig腳本中。
在比特币中另一件令人(rén)討(tǎo)厭的(de)事情是雖然簽名和(hé)公鑰都是512位的(de)橢圓曲線值,但它們的(de)表示方式完全不同:簽名用(yòng)DER編碼方式編碼,而公鑰用(yòng)純字節表示。另外,兩個(gè)值都有一個(gè)額外的(de)字節,但位置并不一緻:SIGHASH_ALL這(zhè)個(gè)附加的(de)哈希值常量放在簽名後面,而04這(zhè)個(gè)值放在公鑰前面。
由于ECDSA算(suàn)法需要使用(yòng)随機數,所以調試簽名十分(fēn)困難。每次計算(suàn)出的(de)簽名都會有所不同,因此無法與已知正确的(de)簽名進行比較。
正是由于上述的(de)複雜(zá)性,我花了(le)很長(cháng)時(shí)間才得(de)到了(le)一個(gè)簽名。不過,最終我找出了(le)簽名代碼中所有的(de)錯誤,并成功用(yòng)它簽署了(le)一筆交易。這(zhè)是我使用(yòng)的(de)簽名代碼:
def makeSignedTransaction(privateKey, outputTransactionHash, sourceIndex, scriptPubKey, outputs): myTxn_forSig = (makeRawTransaction(outputTransactionHash, sourceIndex, scriptPubKey, outputs) + "01000000") # hash code s256 = hashlib.sha256(hashlib.sha256(myTxn_forSig.decode('hex')).digest()).digest() sk = ecdsa.SigningKey.from_string(privateKey.decode('hex'), curve=ecdsa.SECP256k1) sig = sk.sign_digest(s256, sigencode=ecdsa.util.sigencode_der) + '\01' # 01 is hashtype pubKey = keyUtils.privateKeyToPublicKey(privateKey) scriptSig = utils.varstr(sig).encode('hex') + utils.varstr(pubKey.decode('hex')).encode('hex') signed_txn = makeRawTransaction(outputTransactionHash, sourceIndex, scriptSig, outputs) verifyTxnSignature(signed_txn)
return signed2_txn
txnUtils.py
最終的(de)scriptSig腳本中包含簽名以及比特币源地址的(de)公鑰(1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5)。 這(zhè)證明(míng)這(zhè)筆交易有效,我可(kě)以花費這(zhè)些比特币。
PUSHDATA 47 |
47 |
|
signature(DER) |
sequence |
30 |
length |
44 |
|
integer |
02 |
|
length |
20 |
|
X |
2c b2 65 bf 10 70 7b f4 93 46 c3 51 5d d3 d1 6f c4 54 61 8c 58 ec 0a 0f f4 48 a6 76 c5 4f f7 13 |
|
integer |
02 |
|
length |
20 |
|
Y |
6c 66 24 d7 62 a1 fc ef 46 18 28 4e ad 8f 08 67 8a c0 5b 13 c8 42 35 f1 65 4e 6a d1 68 23 3e 82 |
|
SIGHASH_ALL |
01 |
|
PUSHDATA 41 |
41 |
|
public key |
type |
04 |
X |
14 e3 01 b2 32 8f 17 44 2c 0b 83 10 d7 87 bf 3d 8a 40 4c fb d0 70 4f 13 5b 6a d4 b2 d3 ee 75 13 |
|
Y |
10 f9 81 92 6e 53 a6 e8 c3 9b d7 d3 fe fd 57 6c 54 3c ce 49 3c ba c0 63 88 f2 65 1d 1a ac bf cd |
|
最終的(de)scriptPubKey腳本包含成功花費比特币時(shí)必須執行的(de)腳本。需要注意的(de)是,這(zhè)個(gè)腳本将在未來(lái)花費這(zhè)些比特币的(de)時(shí)候執行。它包含以十六進制表示而不是以Base58Check表示的(de)目标地址1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa,腳本的(de)效果是隻有這(zhè)個(gè)目标地址的(de)私鑰所有者才能使用(yòng)比特币,因此目标地址實際上是這(zhè)些比特币的(de)所有者
OP_DUP |
76 |
OP_HASH160 |
a9 |
PUSHDATA 14 |
14 |
public key hash |
c8 e9 09 96 c7 c6 08 0e e0 62 84 60 0c 68 4e d9 04 d1 4c 5c |
OP_EQUALVERIFY |
88 |
OP_CHECKSIG |
ac |
最終的(de)交易
經過上述的(de)一系列操作,我們完成了(le)最終的(de)交易。但是,别忘了(le),此時(shí)的(de)交易還(hái)沒加入區(qū)塊鏈中,接收方還(hái)沒有收到你的(de)比特币。
privateKey = keyUtils.wifToPrivateKey("5HusYj2b2x4nroApgfvaSfKYZhRbKFH41bVyPooymbC6KfgSXdD") #1MMMM
signed_txn = txnUtils.makeSignedTransaction(privateKey,
"81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48", # output (prev) transaction hash 0, # sourceIndex
keyUtils.addrHashToScriptPubKey("1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5"), [[91234, #satoshis
keyUtils.addrHashToScriptPubKey("1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa")]] ) txnUtils.verifyTxnSignature(signed_txn)
print'SIGNED TXN', signed_txn
makeTransaction.py
最終的(de)交易信息如下(xià)所示:
version |
01 00 00 00 |
|
input count |
01 |
|
input |
previous output hash(reversed) |
48 4d 40 d4 5b 9e a0 d6 52 fc a8 25 8a b7 ca a4 25 41 eb 52 97 58 57 f9 6f b5 0c d7 32 c8 b4 81 |
previous output index |
00 00 00 00 |
|
script length |
8a |
|
scriptSig |
47 30 44 02 20 2c b2 65 bf 10 70 7b f4 93 46 c3 51 5d d3 d1 6f c4 54 61 8c 58 ec 0a 0f f4 48 a6 76 c5 4f f7 13 02 20 6c 66 24 d7 62 a1 fc ef 46 18 28 4e ad 8f 08 67 8a c0 5b 13 c8 42 35 f1 65 4e 6a d1 68 23 3e 82 01 41 04 14 e3 01 b2 32 8f 17 44 2c 0b 83 10 d7 87 bf 3d 8a 40 4c fb d0 70 4f 13 5b 6a d4 b2 d3 ee 75 13 10 f9 81 92 6e 53 a6 e8 c3 9b d7 d3 fe fd 57 6c 54 3c ce 49 3c ba c0 63 88 f2 65 1d 1a ac bf cd |
|
sequence |
ff ff ff ff |
|
output count |
01 |
|
output |
value |
62 64 01 00 00 00 00 00 |
script length |
19 |
|
scriptPubKey |
76 a9 14 c8 e9 09 96 c7 c6 08 0e e0 62 84 60 0c 68 4e d9 04 d1 4c 5c 88 ac |
|
block lock time |
00 00 00 00 |
小插曲:橢圓曲線簽名
比特币的(de)簽名算(suàn)法使用(yòng)到了(le)橢圓曲線簽名算(suàn)法,這(zhè)麽實用(yòng)的(de)功能,你可(kě)能會好奇它是怎麽做(zuò)到的(de)?在當年英國數學家安德魯·懷爾斯攻克費馬大(dà)定理(lǐ)時(shí),我第一次接觸到了(le)橢圓曲線的(de)算(suàn)法。橢圓曲線的(de)數學思想很有意思,所以在這(zhè)裏我給大(dà)家做(zuò)一個(gè)快(kuài)速的(de)概述。
橢圓曲線這(zhè)個(gè)叫法令人(rén)迷惑,因爲橢圓曲線并不是橢圓,而且看起來(lái)也(yě)不像橢圓,甚至橢圓曲線與橢圓相關性都很少。通(tōng)俗地講,橢圓曲線就是滿足一個(gè)簡單方程y ^ 2 = x ^ 3 + ax + b的(de)曲線。比特币中使用(yòng)的(de)稱爲secp256k1的(de)橢圓曲線,它滿足的(de)方程爲y ^ 2 = x ^ 3 + 7。
secp256k1橢圓曲線
橢圓曲線的(de)一個(gè)重要特性就是你可(kě)以用(yòng)一個(gè)簡單的(de)規則來(lái)定義橢圓曲線上點的(de)相加:如果在曲線上繪制一條直線,這(zhè)條直線與曲線交與A,B,C三個(gè)點,那麽這(zhè)個(gè)加法定義爲A+B+C=0。由這(zhè)個(gè)加法的(de)定義,我們可(kě)以定義整數乘法:例如4A = A + A + A + A。
爲什(shén)麽橢圓曲線在密碼學上很有用(yòng)?因爲橢圓曲線做(zuò)整數乘法運算(suàn)速度很快(kuài),但做(zuò)除法時(shí)需要蠻力。例如,你可(kě)以快(kuài)速地計算(suàn)一個(gè)乘法12345678*A = Q,但是如果你隻知道A和(hé)Q,求解n*A=Q中的(de)n十分(fēn)困難。因此在橢圓曲線算(suàn)法中,這(zhè)裏的(de)12345678将是私鑰,曲線上的(de)點Q将是公鑰。
在密碼學中,點的(de)坐(zuò)标并不是它在曲線上的(de)實值點,而是對(duì)整數的(de)模數。橢圓曲線的(de)一個(gè)好用(yòng)的(de)特性就是對(duì)實數或模數進行運算(suàn)的(de)數學運算(suàn)幾乎相同。正因爲如此,比特币的(de)橢圓曲線并不像上面的(de)圖片,而是一團雜(zá)亂無章(zhāng)的(de)256位點集(想想在一個(gè)空間中充滿了(le)大(dà)量雜(zá)亂無章(zhāng)的(de)點)。
橢圓曲線數字簽名算(suàn)法(ECDSA)接收交易的(de)哈希值,使用(yòng)該交易數據,私鑰,以及一個(gè)随機數從橢圓曲線上生成一個(gè)新的(de)點,從而實現對(duì)交易的(de)簽名。任何擁有公鑰,交易數據,和(hé)簽名的(de)人(rén)都可(kě)以通(tōng)過做(zuò)一個(gè)簡單的(de)橢圓曲線運算(suàn)來(lái)驗證簽名的(de)有效性。讀到這(zhè)裏,你應該明(míng)白了(le)爲什(shén)麽隻有擁有私鑰的(de)人(rén)才能簽署消息,但擁有公鑰的(de)任何人(rén)都可(kě)以驗證該消息。
把交易發送到比特币網絡
回到交易中來(lái),别忘了(le)此時(shí)我們的(de)交易還(hái)沒有被加入到區(qū)塊鏈中,還(hái)不是一筆有效交易。剛剛我創建并簽署了(le)一筆交易。下(xià)一步就是将這(zhè)筆交易發送到比特币網絡中,網絡中的(de)礦工會收集交易并把它打包進區(qū)塊中。
如何找到比特币網絡的(de)節點
首先我要在比特币的(de)點對(duì)點網絡中找到一個(gè)節點。節點的(de)列表會随節點的(de)進出動态更新,當一個(gè)比特币節點連接到另一個(gè)節點時(shí),它們就會不斷交換彼此新發現的(de)比特币節點信息,因此,新節點加入的(de)消息會快(kuài)速地傳遍整個(gè)網絡。
然而,新的(de)比特币節點如何第一次找到比特币節點?這(zhè)是一個(gè)先有雞還(hái)是先有蛋的(de)問題。比特币節點通(tōng)過以下(xià)幾種方法來(lái)解決這(zhè)個(gè)問題。有幾個(gè)可(kě)信的(de)比特币節點會以bitseed.xf2.org的(de)域名在DNS系統(Domain Name System,域名系統,萬維網上作爲域名和(hé)IP地址相互映射的(de)一個(gè)分(fēn)布式數據庫)上注冊,通(tōng)過執行nslookup命令,你就可(kě)以得(de)到這(zhè)些節點的(de)IP地址,隻要有一個(gè)在工作即可(kě)。如果很不幸它們都沒有工作的(de)話(huà),你可(kě)以試著(zhe)連接那幾個(gè)已經在你的(de)客戶端中硬編碼記錄下(xià)來(lái)的(de)地址。
Nslookup命令可(kě)以用(yòng)來(lái)尋找比特币節點
當用(yòng)戶啓動或停止比特币客戶端時(shí),節點就會加入或離開比特币網絡。所以連接節點有很大(dà)的(de)不确定性,在我實驗時(shí),就遇到了(le)連接的(de)節點已經離開比特币網絡的(de)情況,如果你想重複我的(de)實驗,最好多(duō)找幾個(gè)節點,可(kě)能需要多(duō)次嘗試才能找到一個(gè)運行著(zhe)的(de)節點。
與比特币節點通(tōng)信
一旦獲得(de)了(le)一個(gè)正在工作的(de)比特币節點的(de)IP地址,當務之急就通(tōng)過這(zhè)個(gè)節點是把我的(de)交易發送到比特币的(de)點對(duì)點網絡中。使用(yòng)點對(duì)點的(de)網絡協議(yì)十分(fēn)簡單,我在端口8333上打開了(le)一個(gè)到任意對(duì)等端的(de)TCP連接,發送消息,然後接受反饋消息。比特币的(de)點對(duì)點協議(yì)對(duì)用(yòng)戶很友好,即使我的(de)請求數據出錯時(shí),還(hái)是繼續與我保持通(tōng)信。
重要提示:正如一些人(rén)指出的(de)那樣,如果你想重複我的(de)實驗,切記要使用(yòng)比特币的(de)測試網絡,在測試網絡上,你可(kě)以使用(yòng)“虛拟”的(de)比特币來(lái)進行交易。因爲在真實網絡上,萬一你不小心,有可(kě)能會失去所有的(de)比特币。還(hái)記得(de)上面提到的(de)那個(gè)100個(gè)比特币轉賬1個(gè)的(de)交易麽,如果你忘了(le)将剩餘的(de)比特币轉給自己,那麽剩餘的(de)99個(gè)比特币就會作爲交易費支付給礦工。但是本著(zhe)科學的(de)态度,我并不在意在真實的(de)比特币網絡中損失我這(zhè)些價值1美(měi)元的(de)比特币。
協議(yì)中包含24種不同的(de)信息種類。每一條信息都是一個(gè)簡單的(de)二進制大(dà)對(duì)象(binary large object ,BLOB,是一個(gè)可(kě)以存儲二進制文件的(de)容器),其中包含一個(gè)ASCII命令和(hé)一個(gè)适用(yòng)該命令的(de)二進制有效參數。該協議(yì)可(kě)以在比特币的(de)維基上查詢。
連接到比特币網絡的(de)第一步就是通(tōng)過交換客戶端版本信息來(lái)建立連接。首先,我發送了(le)一條客戶端版本信息,其中包含我的(de)協議(yì)版本号,IP地址和(hé)其他(tā)内容。比特币節點也(yě)向我回複了(le)它的(de)版本信息。在此之後,我應該回複一個(gè)verack信息(version acknowledgement,版本确認)來(lái)确認它的(de)版本信息。正如我所說,比特币點對(duì)點網絡協議(yì)對(duì)用(yòng)戶十分(fēn)友好,即使我跳過了(le)verack信息,之後的(de)操作也(yě)是一切正常。
交換版本信息這(zhè)一步并不簡單,因爲信息具有标準的(de)格式,不過不用(yòng)害怕,可(kě)以用(yòng)幾行代碼來(lái)創建這(zhè)些信息。下(xià)面代碼段中的(de)makeMessage函數可(kě)以由随機數,命令名以及命令的(de)參數來(lái)生成一條消息。getVersionMessage函數通(tōng)過将各個(gè)字段打包在一起來(lái)爲版本消息創建參數。
magic = 0xd9b4bef9
def makeMessage(magic, command, payload): checksum =
hashlib.sha256(hashlib.sha256(payload).digest()).digest()[0:4]
return struct.pack('L12sL4s', magic, command, len(payload), checksum) + payload
def getVersionMsg():
version = 60002 services = 1 timestamp = int(time.time()) addr_me = utils.netaddr(socket.inet_aton("127.0.0.1"), 8333) addr_you = utils.netaddr(socket.inet_aton("127.0.0.1"), 8333) nonce = random.getrandbits(64) sub_version_num = utils.varstr('') start_height = 0 payload = struct.pack('<LQQ26s26sQsL', version, services, timestamp, addr_me, addr_you, nonce, sub_version_num, start_height)
return makeMessage(magic, 'version', payload)
msgUtils.py
發送交易tx
我使用(yòng)下(xià)面精簡的(de)Python代碼把我的(de)交易發送到比特币網絡中,這(zhè)個(gè)代碼發送一條客戶端版本信息,接受(也(yě)可(kě)以忽略)比特币節點的(de)版本信息和(hé)verack信息。最後将我的(de)交易以tx信息發送。代碼中這(zhè)個(gè)16進制的(de)字符串是我之前創建的(de)交易。
def getTxMsg(payload): return makeMessage(magic, 'tx', payload) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("97.88.151.164", 8333)) sock.send(msgUtils.getVersionMsg()) sock.recv(1000) # receive version sock.recv(1000) # receive verack sock.send(msgUtils.getTxMsg("0100000001484d40d45b9ea0d652fca8258ab7caa42541eb52975857f96fb50cd732c8b481000000008a47304402202cb265bf10707bf49346c3515dd3d16fc454618c58ec0a0ff448a676c54ff71302206c6624d762a1fcef4618284ead8f08678ac05b13c84235f1654e6ad168233e8201410414e301b2328f17442c0b8310d787bf3d8a404cfbd0704f135b6ad4b2d3ee751310f981926e53a6e8c39bd7d3fefd576c543cce493cbac06388f2651d1aacbfcdffffffff0162640100000000001976a914c8e90996c7c6080ee06284600c684ed904d14c5c88ac00000000".decode('hex')))
minimalSendTxn.py
以下(xià)Wireshark(一個(gè)抓取,分(fēn)析網絡封包的(de)軟件)軟件的(de)截圖顯示出我是如何将交易發送到比特币網絡中的(de)。我用(yòng)Python編寫了(le)腳本來(lái)分(fēn)析網絡數據,爲了(le)簡單起見,在這(zhè)裏我使用(yòng)Wireshark。從圖中可(kě)以看到我的(de)這(zhè)筆tx交易。
Wireshark中抓取的(de)這(zhè)筆正在上傳至比特币網絡的(de)交易tx
爲了(le)實時(shí)監控我這(zhè)筆交易的(de)進度,我在比特币網絡中新運行了(le)一個(gè)節點,在把我交易發到比特币網絡5秒鐘(zhōng)之後,另一個(gè)節點給我發送了(le)這(zhè)個(gè)tx消息,其中包含我剛剛發送的(de)這(zhè)筆交易的(de)哈希,由此可(kě)見,在僅僅這(zhè)幾秒中,我的(de)交易已經傳遍了(le)比特币網絡,至少也(yě)是比特币網絡的(de)一部分(fēn)。
交易成功:我的(de)交易被加入區(qū)塊鏈
在将我的(de)交易發送比特币網絡之後,我需要等待它被礦工開采出來(lái)加入到區(qū)塊鏈中,然後才能宣稱我的(de)實驗圓滿成功。10分(fēn)鐘(zhōng)後,我的(de)比特币節點收到一條含有新區(qū)塊信息的(de)inv消息(參見下(xià)圖Wireshark抓到的(de)網絡封包),檢查這(zhè)個(gè)區(qū)塊後發現我的(de)交易被包含在了(le)區(qū)塊中,證明(míng)我的(de)交易是有效的(de),我的(de)實驗成功了(le)。通(tōng)過我的(de)比特币錢包軟件和(hé)在線查詢,再一次确認了(le)我已經交易成功。可(kě)以說,經過不斷的(de)努力,我成功手動創建了(le)一筆交易,并讓比特币系統接受了(le)它。(當然了(le),我也(yě)經過了(le)幾次失敗的(de)嘗試,這(zhè)些錯誤的(de)交易都消失在了(le)網絡之中,永遠(yuǎn)都不會被檢索到。
Wireshark中抓取的(de)新區(qū)塊産生的(de)封包信息
我的(de)交易是被當時(shí)哈希算(suàn)力(挖礦速度)最大(dà)的(de)礦池(多(duō)個(gè)礦工一起挖礦)GHash.IO挖出,區(qū)塊高(gāo)度爲279068,區(qū)塊哈希爲0000000000000001a27b1d6eb8c405410398ece796e742da3b3e35363c2219ee,在上圖Wireshark數據包中inv消息的(de)哈希值是經前後反轉得(de)到的(de)ee192……。你應該會發現區(qū)塊的(de)哈希值以大(dà)量的(de)0開頭,在一個(gè)16進制的(de)哈希值中發現一個(gè)以這(zhè)麽多(duō)0開頭的(de)數,這(zhè)就是爲什(shén)麽挖礦如此困難的(de)原因。這(zhè)個(gè)區(qū)塊中由462筆交易,我的(de)交易是其中之一。
高(gāo)度爲279068的(de)區(qū)塊以及我發起的(de)這(zhè)筆交易
(https://blockchain.info/block-index/341440/0000000000000001a27b1d6eb8c405410398ece796e742da3b3e35363c2219ee)
挖到這(zhè)個(gè)區(qū)塊的(de)礦工們收到了(le)25個(gè)比特币的(de)獎勵,交易費總共是0.104個(gè)比特币,按當時(shí)的(de)市價分(fēn)别爲19000美(měi)元和(hé)80美(měi)元。我支付了(le)0.0001個(gè)比特币的(de)交易費,大(dà)約是我交易額的(de)10%,按當時(shí)的(de)市價爲8美(měi)分(fēn)。
結論
手動進行比特币交易比我想象中困難得(de)多(duō),但是在這(zhè)個(gè)過程中我學到了(le)很多(duō),希望你也(yě)是。我的(de)Python代碼僅僅是爲了(le)介紹,如果你想跟我一樣用(yòng)Python手動進行比特币交易,也(yě)可(kě)以試試這(zhè)幾個(gè)項目。
https://en.bitcoin.it/wiki/Bitcoin-python
https://github.com/richardkiss/pycoin
https://github.com/jgarzik/python-bitcoinlib
寫在最後
2017年是區(qū)塊鏈的(de)井噴之年,經過一年的(de)積攢,2018年将迎來(lái)區(qū)塊鏈的(de)落地之年,區(qū)塊鏈會逐漸颠覆各行各業。對(duì)于個(gè)人(rén),區(qū)塊鏈的(de)機會會越來(lái)越多(duō),也(yě)許你錯過了(le)比特币的(de)投資,不妨現在抓住區(qū)塊鏈這(zhè)個(gè)風口,投資自己,多(duō)學習(xí)相關知識,區(qū)塊鏈大(dà)有可(kě)爲,投身區(qū)塊鏈的(de)你将大(dà)有作爲!
*聲明(míng):推送内容及圖片來(lái)源于網絡,部分(fēn)内容會有所改動,版權歸原作者所有,如來(lái)源信息有誤或侵犯權益,請聯系我們删除或授權事宜。