比特幣所使用的數據結構、實際腳本以及語言。
1. 交易
交易有輸入以及輸出,但是輸出並不是輸入的舊值,雖然其中的計算會用到,但是實質我們理解的就是:我們用的舊值去創造舊值而不是新值。同時隨着創造的新值還有他需要指定的地址。
1.1 地址轉換
- 愛麗絲只需付給鮑勃17個幣,但愛麗絲在上一交易中實際獲得了25個幣,爲了把這些幣全部消費掉,她必須再轉給自己8個幣。這8個幣可以轉到另外一個地址(不同於交易1中獲得25個幣的地址),但前提是該地址爲愛麗絲所有;
- 愛麗絲從自己的賬上轉移到了另外一個也屬於自己的地址——這就叫做地址轉換。
1.2 有效驗證
- 要覈查一下愛麗絲引用的交易輸出,確認她確實有25個幣沒有被花費掉;——好處就是不用從最開始查起
- 一直覈查到賬本上最新記錄的交易爲止即可;
1.3 多個輸入或者多個輸出:當存在這樣一種情況的時候,就需要
- 資金合併<==鮑勃在兩筆不同的交易中分別收到17個幣和2個幣,現在他想把這兩筆錢合併起來花掉,兩個輸入和一個輸出,輸出的地址是他自己的地址
- 共同支付<==卡羅爾和鮑勃想要共同支付給戴維,兩個輸入一個輸出
1.4 交易語法[1],[2]
-
真實的比特幣交易片段:
-
從字段可以看出包含3個結構:元數據、輸入、輸出
- 元數據:
- :此筆交易的哈希值,也就是這個交易獨一無二的ID。我們可以用哈希指針指向這個ID。
- :這筆交易的規模
- :輸入的數量
- :輸出的數量
- :版本號
- :最後還有一個“鎖定時間”(lock_time)
- 輸入:
- 可能包含多個輸入,每個輸入包含:
- :上一區塊的以及值(前一個哈希指定的交易中交易輸出的索引。);
- :簽名,證明我們有能力去支配這個區塊,也就是將我們的身份進行認證;
- 可能包含多個輸入,每個輸入包含:
- 輸出:
- 可能包含多個輸出;每個輸出有以下內容:
- :輸出的金額,其中金額的大小是有要求的,輸出的金額之和必須輸入的金額之和
- :可以理解爲某一個腳本的公鑰——指定可以聲明事務輸出的條件。
- 每個輸出都要和一個特定的公鑰(地址)對應,所以這一長串字符裏面確實有一部分看上去是公鑰的哈希值,但裏面還有一部分看上去像指令集合的東西;
- 公鑰+指令集合;它其實是一個比特幣的腳本;
- 可能包含多個輸出;每個輸出有以下內容:
- 元數據:
-
2. 腳本
2.1 常見的比特幣的交易:
2.1.1 常見的一種比特幣交易場景
-
通過某人的簽名去取得他在前一筆交易中獲得的資金,那麼對應的腳本就是:
-
憑藉哈希值爲X的公鑰(所以這裏公鑰就是身份),以及這個公鑰所有者的簽名,纔可以獲得這筆資金。
-
輸出腳本只是指定了一個公鑰(或是公鑰哈希值的地址),輸入腳本指定了一個對應公鑰的簽名
-
2.1.2 幾種內容的比特幣交易
- 銷燬證明
- 能做到什麼?
- 如果交易代碼的運行結果是將比特幣轉到“銷燬證明”腳本,那麼這筆比特幣將被銷燬。
- 如何實現:
- 使用OP_RETURN腳本來拋出錯誤;不論之前指令的運行結果是什麼,OP_RETURN指令總會被執行,並相應拋出一個錯誤,腳本返回一個“錯誤”(false)值。
- 有趣的應用場景:
- 將某一些信息永遠保存在區塊鏈:由於發生錯誤之後的交易也不會對其內容進行增加什麼,
- 實現方式:發起一筆極小額的比特幣交易,在腳本中加入上述信息,並使用銷燬證明腳本將幣銷燬,這樣就可以將信息永久地存儲在區塊鏈上
- 將某一些信息永遠保存在區塊鏈:由於發生錯誤之後的交易也不會對其內容進行增加什麼,
- 能做到什麼?
- 支付給腳本的哈希值(使用)
- 多重簽名地址支付,而且還可以實現複雜的資金監管規則。
- 比特幣使用的辦法是(此時發送的就不是公鑰的hash而是腳本的hash):
- 收款方告訴付款方“請把比特幣支付給某個腳本地址,腳本的哈希值是××,在取款的時候,我會提供上述哈希值對應的腳本,同時,提供數據通過腳本的驗證”
2.2 常用的指令[5]:
指令 | 指令詞源 | 16進制字節碼 | 功能說明 |
---|---|---|---|
duplicate | 0x76 | 複製棧頂元素 | |
hash | 0xa9 | 彈出棧頂元素,先進行SHA-256哈希,再進行RipeMD160哈希處理,將結果壓入棧。 | |
equal verify | 0x88 | 彈出棧頂兩個元素,如果兩個內容一致,則返回1,腳本繼續執行。否則返回0,腳本中斷執行。 | |
check signature | 0xac | 彈出棧頂兩個元素,用公鑰檢查輸入中的簽名,驗證該簽名是否擁有該公鑰的用戶用其私鑰簽署的。如果簽名符合,則將true(true = 1) 壓入棧頂。 | |
check multiple signature | 0xae | 用多個公鑰檢查多重簽名的正確性。 | |
return | 0x6a | 標記交易無效 | |
下面多少個字節要被壓入堆棧 | |||
數字1被壓入堆棧 | |||
一個字節空串被壓入堆棧 |
2.3 執行指令實行堆棧執行,由指令中大量使用堆棧的指令也可以看出來。
-
堆棧過程圖示:
- 尖括號裏的是數據指令,以OP開頭的是工作碼指令,指令上方對應的是指令執行之後的堆棧狀態
- 尖括號裏的是數據指令,以OP開頭的是工作碼指令,指令上方對應的是指令執行之後的堆棧狀態
-
堆棧過程詳解:
- 依次入棧
- 複製棧頂元素並將其置於棧頂;
- 取棧頂元素並計算其hash值,再講計算的hash值放在棧頂;
- 還要在堆棧頂層再推送一些數據:此筆交易發送者指定的公鑰的哈希值,以及對應的私鑰,這樣纔可完成簽名,取得資金。
- 此時的棧頂元素:發送者指定的公鑰的哈希值+接收者想要取得資金時提交的公鑰的哈希值(這兩者應該是一致交易纔會成功)
- 命令:用來判斷棧頂兩個hash值是否是一致
- :驗證簽名是否一致正確(簽名是對交易進行的簽名)
2.4 三種類型的標準腳本[4]:
2.4.1 比特幣實現花錢的方式:
- 花錢的場景:
- 假設B要花A轉給他的錢:
- A在交易M的輸出中,寫一個腳本(輸出腳本),請寫明金額,表示把錢轉給A。
- B在交易N的輸入中,也寫一個腳本(簽名腳本),意思是要花A在交易M中轉給他的錢。
- 當交易M在網上傳播時,比特幣節點驗證交易M,只要簽名腳本符合輸出腳本的要求,節點就認可B能夠執行這個花費。
- 假設B要花A轉給他的錢:
2.4.2 方式分類:P2PKH、P2SH、Multisig
-
(支付到公鑰模式):
-
這種支付,就是支付給某人的公鑰,也就是支付給地址
-
格式:
- 輸出腳本:OP_DUP OP_HASH160 (0x14) [一個20字節的哈希值] OP_EQUALVERIFY OP_CHECKSIG
- 輸入腳本:[簽名的字節數][簽名]0x01 [公鑰的字節數] [公鑰]
-
例子:
OP_DUP OP_HASH160 14 (字節數,0x14 = 20L, 網站在顯示時省略了這一字節) 7232ca33e0797405a512fa872934cd922c812965 (20字節的哈希值,區塊的hash值) OP_EQUALVERIFY OP_CHECKSIG
-
-
(贖回腳本):
- 其實就是相對於的升級將1個公鑰-簽名對升級成了n個公鑰-簽名對;
- 格式:
- 輸出腳本:OP_HASH160 (0x14) [一個20字節的哈希值] OP_EQUAL “簽名腳本”:(輸入腳本)
- 輸入腳本:0x00 [字節數] [簽名1] 0x01 …[字節數] [簽名m] 0x01 [支付合同腳本的字節數] [m] [字節數][公鑰1]… [字節數][公鑰n][n] OP_CHECKSIGVERIFY
-
:
- 這樣的好處就是B就可以隨意定義自己希望的輸出腳本,這就是地址,以數字3開頭的比特幣地址是$地址。
- 也可以實現地址和的功能。
3. 應用
3.1 第三方支付交易
3.1.1 場景:
愛麗絲用比特幣向鮑勃買東西,愛麗絲想貨到付款,而鮑勃想見款發貨。該如何處理?一個好的辦法是使用第三方支付交易(escrow transaction)
3.1.2 實現
3.2 綠色地址
3.2.1 應用場景
在一些交易很小並且不能忍受太長時間的等待的時候,
解決方案:
- 使用類似第三方銀行的方案;
3.1.2 實現
如果愛麗絲要轉賬給鮑勃,愛麗絲會和她的銀行聯繫,“我要付給鮑勃這些幣,你能辦理嗎?”銀行會回答:“好的。我會從你的賬號扣錢,然後從我的綠色地址轉賬給鮑勃。”
3.3 高效小額支付(efficient micro-payments)
3.3.1 應用場景
鮑勃是愛麗絲的手機流量提供商,根據愛麗絲每分鐘使用的流量計費。但是,每分鐘支付一次是不現實的,於是實現一種一段時間累積的方案。
3.3.2 實現
愛麗絲在每分鐘使用的時候,每隔小段時間就進行簽名並且支付,但是這階段是沒有鮑勃的簽名,所以形成不了區塊,只有當愛麗絲使用完成或者愛麗絲沒錢支付了這時候鮑勃進行簽名,完成這筆交易形成區塊。並且愛麗絲單獨的交易並不會進入區塊只有最後整個的交易纔會進入區塊。
3.4 鎖定時間
3.4.1 應用場景
小額支付協議會簽訂一個交易,如果在一定的時間內鮑勃並沒有進行最後簽名,這時候的愛麗絲就會要求退還所有款項,那麼這段時間內對鮑勃的退款行爲就是鎖定的,這裏就是如何實現這個鎖定的功能的。
3.4.2 實現
元數據加入一個參數:lock_time。
在此參數後面填上非零數值t, 這個值告訴礦工在記賬的時候,要等待t時間之後才能把這筆交易記入區塊鏈
3.5 智能合約
比特幣系統裏可以用技術手段來強制自動執行的合約,其他詳細可以看智能合約
4. 區塊
4.1 區塊結構[6]:
-
區塊頭:
- 區塊頭裏面存儲着區塊的頭信息,包含上一個區塊的哈希值(),本區塊體的哈希值(),以及時間戳()等等。
-
區塊體:
-
區塊體存儲着這個區塊的詳細數據(Data),這個數據包含若干行記錄,可以是交易信息,也可以是其他某種信息。
區塊與Hash是一一對應的,Hash可以當做是區塊的唯一標識。
-
4.2 區塊使用的數據結構是:默克爾樹
-
區塊頭部還要包含一個礦工可以修改的“臨時隨機數”、一個時間戳和一個點數(點數用來表示找到這個區塊的難度)。
-
區塊頭部是挖礦過程中唯一哈希值化的,所以要驗證一個區塊的鏈,只要檢查區塊頭部即可。
-
在區塊頭部唯一的交易數據是交易樹的樹根——“mrkl_root”
4.3 幣基交易(獎勵所產生的造幣操作)
每個區塊的梅克爾樹上都有一個有意思的交易,叫作幣基交易(獎勵所產生的造幣操作),與普通交易有所區別:
-
它永遠只有一個單一的輸入與單一的輸出。
-
這個交易並不消費之前交易輸出的比特幣,因此,沒有指針指向“上一交易”。
-
這個輸出值目前大約是25個幣多一點點。這個輸出值就是礦工的挖礦收入。它由兩部分組成:一部分是獎勵的25個比特幣(獎勵在每生產210 000個區塊——大概4年——後減半),另一部分是所有交易的交易手續費。
-
還有一個特別的地方就是“幣基”參數,礦工可以放任何值進去。
5. 網絡
點對點網絡,在比特幣網絡裏,所有的節點都是平等的。沒有等級,也沒有特殊的節點,或所謂的主節點,隨意地組成拓撲,節點加入時與其他所有節點權限一樣,沒有規定哪個節點離開,界線就是超過3小時沒有響應就會被其他節點忘記
5.1 如何加入網絡?
5.2 加入比特網的好處
想讓整個網絡知道一個信息,由於加入了網絡就可以進行“泛洪”操作,其操作流程如下:
- 交易池確保了信息不會無休止地傳播下去;
5.3 加入比特網過程中遇到的問題
5.3.1 節點接收到新交易的時候如何進行檢驗?
- 最重要的一個是交易驗證,也就是驗證交易在當前的區塊鏈中是有效的,節點會針對每個前序交易的輸出運行覈驗腳本,確保腳本的返回值都爲真;
- 檢查是否有雙重支付;
- 如前文所述,節點會檢查這筆交易信息是不是已經被本節點接收過;
- 節點只會接收和傳遞在白名單上的標準腳本
注意:沒有規則強制節點執行這些檢查。雖然如此,每個節點還是有必要進行檢查的,檢驗會帶來更高的安全性
5.3.2 比特幣網絡如何解決競態條件?
-
競態條件指的是什麼?
- 愛麗絲想把同一個比特幣支付給鮑勃與查理,於是,愛麗絲幾乎同時發出兩筆交易。有些節點先聽到愛麗絲→鮑勃交易,有些則先聽到愛麗絲→查理交易,因爲你聽到一個就會把它放入交易池,那麼另外一個就不會加入交易池,由於每一個節點的接收到的次序並不是一致,所以最後導致大家的交易池對於愛麗絲所要發起的交易是哪一筆並不一致。
-
對於比特幣來說,這樣的問題最終是由打包下一區塊的礦工來解決,大致如下圖所示:
位置很重要,如果近的話就能夠在這種發生衝突的情況下先接收到某一個信息
5.3.3 大多數情況基於某一假設
基於一個假設,但是這個假設並不強制
- 不管接收到什麼信息,每個節點均保留最早接收到的交易。但是比特幣網絡是一個對等的網絡,節點並不被強制要求這麼做,任何節點都有權按照其他邏輯行事,並按照所選的邏輯決定到底保留哪個交易、轉播哪些交易
5.3.4 交易的傳播:
- 零驗證交易:一旦交易在網絡中廣播,接收方就立即接受交易
- 由於礦工的缺省行爲是把先接收到的交易放入交易池,這樣,在零驗證交易裏就很難實現重複支付
- 費用替代策略:節點在遇到有衝突的交易時,會把交易手續費更高的交易放進自己的交易池,把手續費更低的替換出去
- 礦工的角度,由於收益更高,因此也是理性的選擇,不過使得雙重支付攻擊更容易了
隨着發展有了“有選擇權的”(opt-in)費用替代策略的做法,也就是交易可以標記自己是否適用費用替代策略
5.3.5 區塊的傳播:
1. 同樣受到競態條件
礦工挖到一個礦(打包一個區塊),然後將區塊加入區塊鏈,這個過程與新交易的傳播過程類似,也受同樣競態條件的限制
- 面臨的狀況:如果兩個有效的區塊同時被挖到(也就是有兩個礦工同時獲得了記賬權力時),只有其中一個區塊可以進入長期共識鏈,納入的就被永久加入區塊鏈,沒納入的就被丟棄
2. 覈實區塊?
- 確認區塊頭部;
- 確定裏面的哈希值是在可以接受的範圍內
- 確認區塊裏的每個交;
- 一個節點往外傳播的區塊必須是最長的一條區塊鏈上新加入的區塊
3. 延時
-
泛洪情況的延時:比較大的區塊需要30秒左右才能傳播到大部分的節點。
-
區塊鏈設計原則:在比特幣的設計裏,簡便是第一位的(簡單的網絡、節點可隨時加入或退出)
4. 網絡大小:
- 很難測量,隨時在變化;
- 往高說,每個月可能有100萬個IP地址成爲比特幣網絡的節點(也可能是臨時成爲節點)。
- 往低說,大約只有5 000~10 000節點永遠在線並處理交易。這個數字有點出乎意料得小
5. 存儲空間需求:
- 完全有效的節點
- 目前的存儲空間大約要幾十個GB,一臺臺式機就能滿足要求
- 完全有效節點必須維護在交易中產生的(交易的輸出)、未被消費掉的比特幣的完整列表,這個列表最好放在內存而非硬盤裏
- 2014年年中,大約有4 400萬的交易被納入區塊鏈,其中有1 200萬個交易產生的比特幣沒有被使用。還好,這個數據不大,可以很容易地放進1G內存裏。
- 輕量節點:
- 定義:輕客戶端,也叫簡單付款驗證(Simple Payment Verification,簡稱SPV)客戶端,只存儲它們所關心的、需要進行覈驗的部分交易。
- 使用場景:如果你使用一個錢包軟件,那裏面就會有一個SPV節點,這個節點只會下載向你的賬戶付款的交易及區塊頭部。
- 優點以及不足:
- 不足的地方:
- 可以覈驗那些很難被挖到的區塊——因爲它有區塊頭部數據,但它不能覈驗一個區塊裏所有交易記錄的有效性;
- 必須依賴那些全節點去驗證網絡上的其他所有交易
- 優點:
- 輕量節點依賴全節點去處理那些比較難的工作,但當某個區塊由於某些原因未被礦工挖出來時(挖礦成本巨大),這些輕量節點也會做一些覈驗來確保這個區塊不會被拒絕
- 不足的地方:
注意:比特幣是一個開源協議,人們用不同的語言去實現,即使在同一時間,大家運行的客戶端都略有不同。
6. 限制優化
6.1 限制的原因
初始設定的不完善,但是由於巨大的經濟效應使得有一部分是沒法進行改變的,比如:比特幣的總體數量與記賬獎勵很可能永遠都不會改變。
但是有些合理並且能夠進行改善的,我們是有必要讓其改善過來。
- 每個區塊大小限定在1MB,每個交易大約是250字節,所以每塊最多容納4 000個交易。平均每隔10分鐘,有一個礦工獲得記賬權利,所以每秒鐘只能處理7個交易,這就是比特幣網絡的交易處理能力
- 密碼算法:目前就只有幾個hash函數和一個簽名算法(種secp256k1的橢圓曲線數字簽名算法)
6.2 如何對限制進行修訂以及具有哪些相關策略
6.2.1 修訂版本存在的問題
- 最大的問題是由於區塊鏈的特性,有些節點沒有在線,那麼久存在一些節點已經更新修改的,但是有一些卻是沒有更新
6.2.2 兩種修訂版本的方式:硬分叉和軟分叉
1. 硬分叉:
即運行新版協議的節點認定爲有效的區塊,會被運行舊版協議的節點認定爲無效,不斷隨着大部分節點更新,最終的結果就是新的區塊更長,那麼原本那些沒有更新就會被丟棄
2. 軟分叉:
加入新的特性,讓現有的核驗規則更加嚴格。那樣老的節點依然會接收所有的區塊,而新的節點會拒絕一些——避免硬分叉所造成的永久分裂。
老節點可能會挖到一些無效的區塊——因爲這些區塊中包含一些在新規則下無法覈驗通過的交易,這時候老節點意識到自己的區塊分支需要進行更新,於是更新區塊分支最後就與大部分節點一致了,缺點就是這種情況偶有出現,會出現比較多的分支但是解決了老節點沒有更新遭到被丟棄這一情況。
參考文章
3.比特幣系統的腳本(Script)——交易生成和驗證的原理
4.三種類型的簽名
6.區塊頭百度百科