理解交易對比特幣系統是如何工做的是很是重要的,能夠說比特幣整個的工做流程就是圍繞着交易展開的。下面咱們先敘述一下比特幣交易流程,在宏觀上對交易有個認識,後面會講一下交易數據結構,具體在程序中是怎麼定義的。下面先讓咱們看一下比特幣交易流程。html
比特幣交易並非一般意義上的一手交錢一手交貨的交易,而是轉帳。若是每一筆轉帳都須要構造一筆交易數據會比較笨拙,爲了使得價值易於組合與分割,比特幣的交易被設計爲能夠歸入多個輸入和輸出,即一筆交易能夠轉帳給多我的。從生成到在網絡中傳播,再到經過工做量證實、整個網絡節點驗證,最終記錄到區塊鏈,就是區塊鏈交易的整個生命週期。整個區塊鏈交易流程以下圖所示:
c++
比特幣難度調整公式(新難度計算公式): .web
到這裏,你已經理解了一筆交易從建立到寫入區塊鏈的整個流程,下面咱們討論一下其中的細節。先看一下交易的數據結構。算法
一筆比特幣交易是一個含有輸入和輸出的數據結構,直白點就是要說明清楚這邊交易的錢從哪裏來,錢到哪裏去。咱們看一下在源碼中交易的字段有那些,主要字段:編程
vin
vout
nVersion
nLockTime
/** The basic transaction that is broadcasted on the network and contained in * blocks. A transaction can contain multiple inputs and outputs. */ class CTransaction { public: // Default transaction version. static const int32_t CURRENT_VERSION=2; // Changing the default transaction version requires a two step process: first // adapting relay policy by bumping MAX_STANDARD_VERSION, and then later date // bumping the default CURRENT_VERSION at which point both CURRENT_VERSION and // MAX_STANDARD_VERSION will be equal. static const int32_t MAX_STANDARD_VERSION=2; // The local variables are made const to prevent unintended modification // without updating the cached hash value. However, CTransaction is not // actually immutable; deserialization and assignment are implemented, // and bypass the constness. This is safe, as they update the entire // structure, including the hash. const std::vector<CTxIn> vin; const std::vector<CTxOut> vout; const int32_t nVersion; const uint32_t nLockTime; private: /** Memory only. */ const uint256 hash; const uint256 m_witness_hash; uint256 ComputeHash() const; uint256 ComputeWitnessHash() const; public: // ...... 下面代碼這裏省略掉了...... };
這裏先解釋一下nLockTime
字段,後面若是學習閃電網絡的話會用到這個字段。瀏覽器
// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. static const unsigned int LOCKTIME_THRESHOLD = 500000000;
鎖定時間定義了能被加到區塊鏈裏最先的交易時間。在大多數交易裏,它被設置成0,表示當即執行。若是鎖定時間大於0且小於5億,就被視爲區塊高度,在此區塊高度以前,該交易不能被包含在區塊鏈裏。若是鎖定時間大於5億,則被當作一個UNIX時間戳(1970年1月1日以來的秒數),在這個時間點以前,該交易不能被包含在區塊鏈裏。安全
協議版本nVersion
很好理解,就是明確這筆交易參照的規則協議。微信
最重要的是交易輸入和輸出,在分析交易輸入和輸出以前咱們先使用BitcoinCore的命令行界面(getrawtransaction
和decodeawtransaction
)來檢索一筆「原始」交易,對其進行解碼,並查看它包含的內容。 交易被解碼後是這個樣子,結果以下:網絡
你也能夠經過區塊鏈瀏覽器查看這筆交易(塊高度: 277316 , 交易ID :0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2)數據結構
{ "version": 1, "locktime": 0, "vin": [ { "txid":"7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18", // 交易ID "vout": 0, // 輸出索引(vout),用於標識來自該交易的哪一個UTXO被引用(第一個爲零) "scriptSig": "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf", // 解鎖腳本,知足放置在UTXO上的條件,解鎖它用於支出 "sequence": 4294967295 // 序列號 } ], "vout": [ { "value": 0.01500000, "scriptPubKey": "OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 OP_EQUALVERIFY OP_CHECKSIG" }, { "value": 0.08450000, "scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG", } ] }
到這裏,你對交易應該有了一個感性的認識了,這筆交易有1個輸入,2個輸出。下面咱們接着看交易輸入輸出。
/** An input of a transaction. It contains the location of the previous * transaction's output that it claims and a signature that matches the * output's public key. */ class CTxIn { public: COutPoint prevout; // 指向輸入中引用的UTXO CScript scriptSig; // 解鎖腳本,首先檢索引用的UTXO,檢查其鎖定腳本,而後使用它來構建所需的解鎖腳本以知足此要求。 uint32_t nSequence; // 序列號 CScriptWitness scriptWitness; //!< Only serialized through CTransaction ,見證腳本 //.......省略下面部分代碼............. }; /** An outpoint - a combination of a transaction hash and an index n into its vout */ class COutPoint { public: uint256 hash; // 交易ID uint32_t n; // 輸出索引,代表是交易中的第幾個輸出 // ......省略下面部分代碼...... };
能夠看到,交易輸入就是上一筆交易的輸出,須要構建解鎖腳本以證實你對該筆輸出的全部權,而後你能夠花費這筆輸出。
/** An output of a transaction. It contains the public key that the next input * must be able to sign with to claim it. */ class CTxOut { public: CAmount nValue; // 輸出金額 CScript scriptPubKey; // 鎖定腳本(腳本公鑰) ,對於一個比特幣交易來講,交易自己是不用關心輸出的地址,交易只須要關心鎖定腳本,當使用的時候能使用正確的解鎖腳本便可動用比特幣。 // ......省略下面部分代碼...... };
輸出更簡單,就是指定轉帳金額,輸出給誰,構建一個鎖定腳本,只有轉帳接收者才能解鎖成功。擁有該輸出金額的全部權。
能夠看到,交易的數據結構沒有交易費的字段。相替代地,交易費是指輸入和輸出之間的差值。從全部輸入中扣掉全部輸出以後的多餘的量會被礦工做爲交易費收集走:
交易費即輸入總和減輸出總和的餘量:交易費 = 求和(全部輸入) - 求和(全部輸出)
交易費,最直接的影響是與交易被處理的優先級有關,交易費用高的交易會被礦工優先打包處理,會在較短的時間通過驗證上鍊,而交易費用低的交易則會最後處理。同時,交易費用的設置,使得在比特幣網絡上發交易是有成本的,避免了大量可能的無用交易。
可經過區塊鏈瀏覽器查詢每一筆比特幣交易,比特幣區塊鏈瀏覽器有不少,這裏使用 https://btc.com 進行查詢。
腳本是區塊鏈上實現自動驗證、自動執行合約的重要技術。腳本相似一套規則,它約束着接收方怎樣才能花掉這個輸出上鎖定的資產。交易和合法性驗證也依賴於腳本。咱們看一下比特幣交易中的鎖定腳本和解鎖腳本。
鎖定腳本是在輸出交易上加上的條件,經過一段腳本語言來實現,位於交易的輸出。解鎖腳本只有知足鎖定腳本要求的條件,才能花掉這個腳本上對應的資產,位於交易的輸入。解釋腳本是經過相似編程領域裏的「虛擬機」,它分佈式運行在區塊鏈網絡裏的每個節點。
每個比特幣驗證節點會經過同時執行鎖定和解鎖腳原本驗證一筆交易。每一個輸入都包含一個解鎖腳本,並引用了以前存在的UTXO。 驗證軟件將複製解鎖腳本,檢索輸入所引用的UTXO,並從該UTXO複製鎖定腳本。而後依次執行解鎖和鎖定腳本。若是解鎖腳本知足鎖定腳本條件,則輸入有效。全部輸入都是獨立驗證的,做爲交易整體驗證的一部分。
下圖是最多見類型的比特幣交易(P2PKH:對公鑰哈希的付款)的解鎖和鎖定腳本的示例,顯示了在腳本驗證以前從解鎖和鎖定腳本的並置產生的組合腳本:
比特幣的腳本語言被稱爲基於堆棧的語言,由於它使用一種被稱爲堆棧的數據結構。腳本語言經過從左到右處理每一個項目來執行腳本。數字(數據常量)被推到堆棧上。操做碼(Operators)從堆棧中推送或彈出一個或多個參數,對其進行操做,並可能將結果推送到堆棧上。
在最第一版本的比特幣客戶端中,解鎖和鎖定腳本是以連鎖的形式存在,並被依次執行的。出於安全因素考慮,比特幣開發者們修改了這個特性——由於存在「容許異常解鎖腳本推送數據入棧而且污染鎖定腳本」的漏洞。而在當前的方案中,這兩個腳本是隨着堆棧的傳遞被分別執行的。
首先,使用堆棧執行引擎執行解鎖腳本。若是解鎖腳本在執行過程當中未報錯(例如:沒有「懸掛」操做碼),則複製主堆棧(而不是備用堆棧),並執行鎖定腳本。若是從解鎖腳本中複製而來的堆棧數據執行鎖定腳本的結果爲「TRUE",那麼解鎖腳本就成功地知足了鎖定腳本所設置的條件,所以,該輸入是一個能使用該UTXO的有效受權。若是在合併腳本後的結果不是」TRUE「之外的任何結果,輸入都是無效的,由於它不能知足UTXO中所設置的使用該筆資金的條件。
下面以最多見類型的比特幣交易(P2PKH:對公鑰哈希的付款)的解鎖和鎖定腳本爲例(見上圖),說明執行過程。
上面講的都是普通狀況下的交易,但有一個例外,即存在一種被稱爲「幣基交易」(Coinbase Transaction)的特殊交易,它是每一個區塊中的第一筆交易,這種交易存在的緣由是做爲對挖礦的獎勵,創造出全新的可花費比特幣用來支付給「贏家」礦工。
爲何比特幣交易要設計成這種輸入輸出的結構,與以太坊等區塊鏈的結構是不一樣的。個人理解,是要回到比特幣設計的初衷,比特幣是一個非中心化的點對點電子支付系統,非中心化實現這一系統,最大的問題在於如何解決雙花問題。固然,如何解決雙花問題不是僅僅設計一個交易數據結構就能夠解決的,還須要結合Pow共識算法及整個比特幣區塊鏈實現流程。設計成這種數據結構,一個最大的好處是每筆交易的比特幣都是可追溯的,你甚至一直能夠追溯到它的誕生,挖礦獎勵。由於每一筆交易(花費的比特幣)都是可追溯的,做弊者就沒法構造一個無中生有的比特幣,它沒法構造出合法的未花費交易輸出。每一筆交易都會經過廣播盡力廣播給全部比特幣節點。
若是您尚未理解,沒有關係,由於僅僅掌握比特幣交易數據結構是不夠的,它只是比特幣區塊鏈系統設計的一部分,另外歡迎搜索並關注微信公衆號:讓我思考一下,獲取更多區塊鏈相關技術分享。