比特幣交易

理解交易對比特幣系統是如何工做的是很是重要的,能夠說比特幣整個的工做流程就是圍繞着交易展開的。下面咱們先敘述一下比特幣交易流程,在宏觀上對交易有個認識,後面會講一下交易數據結構,具體在程序中是怎麼定義的。下面先讓咱們看一下比特幣交易流程。html

比特幣交易流程

比特幣交易並非一般意義上的一手交錢一手交貨的交易,而是轉帳。若是每一筆轉帳都須要構造一筆交易數據會比較笨拙,爲了使得價值易於組合與分割,比特幣的交易被設計爲能夠歸入多個輸入和輸出,即一筆交易能夠轉帳給多我的。從生成到在網絡中傳播,再到經過工做量證實、整個網絡節點驗證,最終記錄到區塊鏈,就是區塊鏈交易的整個生命週期。整個區塊鏈交易流程以下圖所示:
這裏寫圖片描述c++

  1. 交易的生成——構造一筆交易,設置交易的輸入輸出,並簽名確認。
  2. 交易的傳播——A將交易廣播至全網,節點收到交易驗證經過後再廣播給其餘節點,並都將收到的交易歸入一個區塊中等待工做量證實。(對B而言,該筆比特幣會即時顯示在比特幣錢包中,但直到區塊確認成功後纔可用。目前一筆比特幣從支付到最終確認成功,通常要通過6個區塊確認以後才能真正確認到帳)
  3. 工做量證實——每一個節點經過至關於解一道數學題的工做量證實機制,從而得到建立新區塊的權力,並爭取獲得數字貨幣的獎勵,交易上鍊。(新比特幣在此過程當中產生)
  4. 整個網絡節點驗證——當一個節點找到解時,它就向全網廣播該區塊,並由全網其餘節點驗證。若是驗證經過則向其餘節點廣播並進行第5步。
  5. 記錄到區塊鏈——上一步驗證經過後,將新區塊加入到最長的區塊鏈中。以後繼續競爭下一個區塊,這樣就造成了一個合法記帳的區塊鏈。(每一個區塊的建立時間大約在10分鐘。隨着全網算力的不斷變化,每一個區塊的產生時間會隨算力加強而縮短、隨算力減弱而延長。其原理是根據最近產生的2016個區塊的時間差,自動調整每一個區塊的生成難度,使得每一個區塊的生成時間是10分鐘)

比特幣難度調整公式(新難度計算公式): N e w   D i f f i c u l t y = O l d   D i f f i c u l t y × ( A c t u a l   T i m e   o f   L a s t   2016   B l o c k s / 20160   m i n u t e s ) New \ Difficulty=Old \ Difficulty \times (Actual \ Time\ of\ Last\ 2016\ Blocks/20160 \ minutes) .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的命令行界面(getrawtransactiondecodeawtransaction)來檢索一筆「原始」交易,對其進行解碼,並查看它包含的內容。 交易被解碼後是這個樣子,結果以下:網絡

你也能夠經過區塊鏈瀏覽器查看這筆交易(塊高度: 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:對公鑰哈希的付款)的解鎖和鎖定腳本的示例,顯示了在腳本驗證以前從解鎖和鎖定腳本的並置產生的組合腳本:
image

比特幣的腳本語言被稱爲基於堆棧的語言,由於它使用一種被稱爲堆棧的數據結構。腳本語言經過從左到右處理每一個項目來執行腳本。數字(數據常量)被推到堆棧上。操做碼(Operators)從堆棧中推送或彈出一個或多個參數,對其進行操做,並可能將結果推送到堆棧上。

解鎖和鎖定腳本的單獨執行

在最第一版本的比特幣客戶端中,解鎖和鎖定腳本是以連鎖的形式存在,並被依次執行的。出於安全因素考慮,比特幣開發者們修改了這個特性——由於存在「容許異常解鎖腳本推送數據入棧而且污染鎖定腳本」的漏洞。而在當前的方案中,這兩個腳本是隨着堆棧的傳遞被分別執行的。

首先,使用堆棧執行引擎執行解鎖腳本。若是解鎖腳本在執行過程當中未報錯(例如:沒有「懸掛」操做碼),則複製主堆棧(而不是備用堆棧),並執行鎖定腳本。若是從解鎖腳本中複製而來的堆棧數據執行鎖定腳本的結果爲「TRUE",那麼解鎖腳本就成功地知足了鎖定腳本所設置的條件,所以,該輸入是一個能使用該UTXO的有效受權。若是在合併腳本後的結果不是」TRUE「之外的任何結果,輸入都是無效的,由於它不能知足UTXO中所設置的使用該筆資金的條件。

下面以最多見類型的比特幣交易(P2PKH:對公鑰哈希的付款)的解鎖和鎖定腳本爲例(見上圖),說明執行過程。
image

image

Coinbase交易

上面講的都是普通狀況下的交易,但有一個例外,即存在一種被稱爲「幣基交易」(Coinbase Transaction)的特殊交易,它是每一個區塊中的第一筆交易,這種交易存在的緣由是做爲對挖礦的獎勵,創造出全新的可花費比特幣用來支付給「贏家」礦工。

引伸思考

爲何比特幣交易要設計成這種輸入輸出的結構,與以太坊等區塊鏈的結構是不一樣的。個人理解,是要回到比特幣設計的初衷,比特幣是一個非中心化的點對點電子支付系統,非中心化實現這一系統,最大的問題在於如何解決雙花問題。固然,如何解決雙花問題不是僅僅設計一個交易數據結構就能夠解決的,還須要結合Pow共識算法及整個比特幣區塊鏈實現流程。設計成這種數據結構,一個最大的好處是每筆交易的比特幣都是可追溯的,你甚至一直能夠追溯到它的誕生,挖礦獎勵。由於每一筆交易(花費的比特幣)都是可追溯的,做弊者就沒法構造一個無中生有的比特幣,它沒法構造出合法的未花費交易輸出。每一筆交易都會經過廣播盡力廣播給全部比特幣節點。

若是您尚未理解,沒有關係,由於僅僅掌握比特幣交易數據結構是不夠的,它只是比特幣區塊鏈系統設計的一部分,另外歡迎搜索並關注微信公衆號:讓我思考一下,獲取更多區塊鏈相關技術分享。