生活類比
首先看這個(gè)名詞UTXO (Unspent Transaction Output, “未花費(fèi)事務(wù)輸出”)欲侮,老實(shí)說殉农,第一次看到這個(gè)術(shù)語的時(shí)候茎匠,一時(shí)之間真是有些懵吁朦,如果說是未花費(fèi)的余額還能理解忍啤,我錢包里有1000编检,花了200胎食,還有800未花費(fèi),這是很符合通常的理解邏輯的允懂,可這個(gè)未花費(fèi)的“事務(wù)輸出”是個(gè)什么意思厕怜,實(shí)際上,這與比特幣中的交易事務(wù)結(jié)構(gòu)是很有關(guān)系的。為了讓大家更容易理解粥航,我們暫且先不來解析這個(gè)交易數(shù)據(jù)結(jié)構(gòu)琅捏,讓我們進(jìn)入到一個(gè)倉庫,我們知道倉庫的主要業(yè)務(wù)就是進(jìn)和出递雀,倉庫也會(huì)把日常的進(jìn)出流水賬記錄下來柄延, 為了查詢統(tǒng)計(jì)方便, 除了流水賬通常還會(huì)匯總一 份庫存表出來缀程,舉例如下:
以上圖為例搜吧,這是從2017-8- 1倒2017-8- 5之間,倉庫記錄的出入流水賬杨凑, 為了統(tǒng)計(jì)方便滤奈,倉庫還匯總保存了一份每天的庫存日?qǐng)?bào)表,如下:
每天倉庫在需要出庫的時(shí)候撩满,只要查看一下庫存日?qǐng)?bào)表就知道數(shù)量是否足夠了蜒程,比如2017-8-3需要出庫15支毛筆,此時(shí)查看庫存表發(fā)現(xiàn)毛筆的庫存量有30支伺帘,足夠發(fā)出搞糕,于是就將庫存表中的毛筆數(shù)量減掉15,并且將出庫明細(xì)記錄在流水賬中曼追。然而窍仰,這里有一個(gè)問題,庫存日?qǐng)?bào)表是另外編制保存的礼殊,那就有可能發(fā)生數(shù)據(jù)不一致的情況驹吮,比如2017-8-2時(shí)毛筆的庫存本來是30卻誤寫為20,這樣導(dǎo)致后續(xù)的賬務(wù)就都是錯(cuò)的了晶伦。因此在有些系統(tǒng)中碟狞,為了防止出現(xiàn)這樣的不一致,索性不再另外保存庫存表婚陪,而只是出一張視圖統(tǒng)計(jì)(邏輯統(tǒng)計(jì)族沃,并非實(shí)際去保存這樣一個(gè)統(tǒng)計(jì)比特幣中的交易事務(wù)過程與.上述的庫存進(jìn)出是很相像的,某個(gè)錢包地址中轉(zhuǎn)入了一筆比特幣泌参,然后這個(gè)地址又向其他錢包地址轉(zhuǎn)出了一筆比特幣脆淹,這些不斷發(fā)生的入和出跟倉庫的進(jìn)出是異曲同工的。然而沽一,在比特幣中并沒有去保存份“庫存表 ”每當(dāng)“出庫”的時(shí)候也并不是去“庫存表”中進(jìn)行扣除盖溺,而是直接消耗“入庫記錄”,也就是說在出庫的時(shí)候就去找有沒有之前的入庫記錄拿來扣除铣缠,比如2017-8-3時(shí)需要出庫15支毛筆烘嘱,此時(shí)系統(tǒng)就會(huì)去搜索之前的入庫記錄昆禽,發(fā)現(xiàn)有2017-8-1和2017-8-2分別有一筆數(shù)量為10和20的入庫記錄,為了滿足15的發(fā)出數(shù)量蝇庭,首先可以消耗掉10的這一筆醉鳖,然后從20的這筆再消耗掉5支, 判斷成功后哮内, 系統(tǒng)會(huì)直接產(chǎn)生一條數(shù)量為10的出庫記錄和數(shù)量為5的出庫記錄盗棵,按這樣的方法,將每一筆入和出都對(duì)應(yīng)了起來牍蜂。在比特幣的交易事務(wù)結(jié)構(gòu)中,“入”就是指金額轉(zhuǎn)入泰涂,“出”就是指金額轉(zhuǎn)出鲫竞,為了讓大家對(duì)這種金額轉(zhuǎn)入與轉(zhuǎn)出有一個(gè)更加通俗的理解,我們來看一幅 示意圖:
上圖展示 了比特幣中的交 易事務(wù)結(jié)構(gòu)逼蒙,在比特幣的交易事務(wù)數(shù)據(jù)中从绘, 存儲(chǔ)的就是這樣的輸入和輸出,相當(dāng)于倉庫中的進(jìn)出流水賬是牢,并且“輸入”和“輸出’彼此對(duì)應(yīng)僵井,或者更準(zhǔn)確地說,“輸入”就是指向之前的“ 輸出”驳棱,我們解釋一下圖中發(fā)生的交易事務(wù)批什。
001號(hào)交易為Coinbase交易,也就是挖礦交易社搅,在這個(gè)交易中驻债,“輸入”部分沒有對(duì)應(yīng)的“輸出”,而是由系統(tǒng)直接獎(jiǎng)勵(lì)發(fā)行比特幣形葬,礦工Alice得到了12.5個(gè)比特幣的獎(jiǎng)勵(lì)合呐,放在001號(hào)交易的“輸出” 部分。此時(shí)笙以,對(duì)于Alice來說淌实, 擁有了這12.5個(gè)比特幣的支配權(quán),這12.5個(gè)比特幣的輸出可以作為下一筆交易的“輸入”猖腕,顧名思義拆祈,這筆“輸出”就稱之為是Alice的未花費(fèi)輸出,也就是Alice的UTXO的意思倘感。
002號(hào)交易中缘屹,Alice轉(zhuǎn) 賬6比特幣到Bob的地址,Alice找到 了自己的UTXO (如果Alice不止一筆UTXO侠仇,可以根據(jù)一定的規(guī)則去選用轻姿,比如將小金額的先花費(fèi)掉)犁珠。由于只需要轉(zhuǎn)賬6比特幣,可是UTXO中卻有12.5個(gè)互亮,因此需要找零6.5個(gè)到自己的地址中犁享,由此產(chǎn)生了002號(hào)中的交易輸出,注意豹休,在002號(hào)交易輸出中的Alice地址是可以和001號(hào)中的Alice地址不一樣的炊昆,只要都是屬于Alice自己的錢包地址就可以。
003號(hào)交易中威根,Bob轉(zhuǎn)賬了2比特幣到Lily的地址凤巨,過程與002號(hào)交易相同,就不再贅述了洛搀。相信大家看到這里敢茁,已經(jīng)基本理解了所謂的UTXO是什么意思,我們?cè)賮砜偨Y(jié)一下留美。
1)比特幣的交易中不是通過賬戶的增減來實(shí)現(xiàn)的彰檬,而是一筆筆關(guān)聯(lián)的輸入/輸出交易事務(wù)。
2)每一筆的交易都要花費(fèi)“輸入”谎砾,然后產(chǎn)生“輸出”逢倍,這個(gè)產(chǎn)生的“輸出”就是所謂的“未花費(fèi)過的交易輸出”,也就是UTXO景图。每一筆交易事務(wù)都有一個(gè)唯一的編號(hào)较雕,稱為交易事務(wù)ID,這是通過哈希算法計(jì)算而來的,當(dāng)需要引用某一筆交易事務(wù)中的“輸出”時(shí)挚币,主要提供交易事務(wù)ID和所處“輸出”列表中的序號(hào)就可以了郎笆。
3)由于沒有賬戶的概念,因此當(dāng)“輸入”部分的金額大于所需的“輸出”時(shí)忘晤,必須給自己找零宛蚓,這個(gè)找零也是作為交易的一部分包含在‘輸出”中。有朋友會(huì)問:這個(gè)UTXO的意思是明白了设塔,可是就這么一條條的“輸入”和“輸出怎么證明哪一條UTXO是屬于誰的呢?
在比特幣中凄吏,是使用輸入腳本和輸出腳本程序?qū)崿F(xiàn)的,有時(shí)候也稱為“鎖定腳本”和“解鎖腳本”闰蛔。簡(jiǎn)單地說痕钢,就是通過“鎖定腳本”,利用私鑰簽名解鎖自己的某一條UTXO (也就是之前的“輸出”)序六,然后使用對(duì)方的公鑰鎖定新的“輸出”任连,成功后,這筆新的“輸出”就成為了對(duì)方的UTXO例诀。同樣随抠,對(duì)方也可以使用“鎖定腳本”和“解鎖腳本”來實(shí)現(xiàn)轉(zhuǎn)賬裁着。這個(gè)腳本程序其實(shí)本質(zhì)上就可以看成是比特幣中的數(shù)字合約,這也是為什么比特幣被稱為可編程數(shù)字貨幣的原因拱她,它的轉(zhuǎn)入/轉(zhuǎn)出或者說輸入/輸出是通過腳本程序的組合來自動(dòng)實(shí)現(xiàn)的二驰,實(shí)現(xiàn)過程中還使用到了私鑰和公鑰,也就是公開密鑰算法秉沼,所以比特幣還稱為可編程加密數(shù)字貨幣桶雀。
具體模型
比特幣的區(qū)塊鏈由一個(gè)個(gè)區(qū)塊串聯(lián)構(gòu)成,而每個(gè)區(qū)塊又包含一個(gè)或多個(gè)交易唬复。
任何一個(gè)交易矗积,它總是由若干個(gè)輸入(Input)和若干個(gè)輸出(Output)構(gòu)成,一個(gè)Input指向的是前面區(qū)塊的某個(gè)Output敞咧,只有Coinbase交易(礦工獎(jiǎng)勵(lì)的鑄幣交易)沒有輸入棘捣,只有憑空輸出。所以妄均,任何交易柱锹,總是可以由Input溯源到Coinbase交易哪自。
這些交易的Input和Output總是可以串聯(lián)起來:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│Block #1 │ │Block #2 │ │Block #3 │ │Block #4 │
│┌──┬────┬───┐│ │┌──┬────┬───┐│ │┌──┬────┬───┐│ │┌──┬────┬───┐│
││CB│50.0│OUT├┼──┐ ││CB│50.0│OUT├┼──┐ ││CB│50.0│OUT├┼──┐ ││CB│50.0│OUT││
│└──┴────┴───┘│ │ │└──┴────┴───┘│ │ │└──┴────┴───┘│ │ │└──┴────┴───┘│
│ │ │ │┌──┬────┬───┐│ │ │┌──┬────┬───┐│ │ │┌──┬────┬───┐│
│ │ │ ││ │8.70│OUT├┼──┼──>│IN│ │ ││ └──>│IN│25.0│OUT││
│ │ └──>│IN├────┼───┤│ │ │├──┤58.7│OUT││ │├──┼────┼───┤│
│ │ ││ │41.3│OUT├┼─┐└──>│IN│ │ ││ ┌──>│IN│66.3│OUT││
│ │ │└──┴────┴───┘│ │ │└──┴────┴───┘│ │ │└──┴────┴───┘│
└─────────────┘ └─────────────┘ │ └─────────────┘ │ └─────────────┘
└────────────────────┘
還沒有被下一個(gè)交易花費(fèi)的Output被稱為UTXO:Unspent TX Output丰包,即未花費(fèi)交易輸出。給定任何一個(gè)區(qū)塊壤巷,計(jì)算當(dāng)前所有的UXTO金額之和邑彪,等同于自創(chuàng)世區(qū)塊到給定區(qū)塊的挖礦獎(jiǎng)勵(lì)之和。
因此胧华,比特幣的交易模型和我們平時(shí)使用的銀行賬號(hào)有所不同寄症,它并沒有賬戶這個(gè)說法,只有UTXO矩动。想要確定某個(gè)人擁有的比特幣有巧,并無法通過某個(gè)賬戶查到,必須知道此人控制的所有UTXO金額之和悲没。
在錢包程序中篮迎,錢包管理的是一組私鑰,對(duì)應(yīng)的是一組公鑰和地址示姿。錢包程序必須從創(chuàng)世區(qū)塊開始掃描每一筆交易甜橱,如果:
- 遇到某筆交易的某個(gè)Output是錢包管理的地址之一,則錢包余額增加栈戳;
- 遇到某筆交易的某個(gè)Input是錢包管理的地址之一岂傲,則錢包余額減少。
錢包的當(dāng)前余額總是錢包地址關(guān)聯(lián)的所有UTXO金額之和子檀。
如果剛裝了一個(gè)新錢包镊掖,導(dǎo)入了一組私鑰乃戈,在錢包掃描完整個(gè)比特幣區(qū)塊之前,是無法得知當(dāng)前管理的地址余額的堰乔。那么偏化,給定一個(gè)地址,要查詢?cè)摰刂返挠囝~镐侯,難道要從頭掃描幾百GB的區(qū)塊鏈數(shù)據(jù)侦讨?
當(dāng)然不是。
要做到瞬時(shí)查詢苟翻,我們知道韵卤,使用關(guān)系數(shù)據(jù)庫的主鍵進(jìn)行查詢,由于用了索引崇猫,速度極快沈条。
因此,對(duì)區(qū)塊鏈進(jìn)行查詢之前诅炉,首先要掃描整個(gè)區(qū)塊鏈蜡歹,重建一個(gè)類似關(guān)系數(shù)據(jù)庫的地址-余額映射表。這個(gè)表的結(jié)構(gòu)如下:
address | balance | lastUpdatedAtBlock |
---|---|---|
address-1 | 50.0 | 0 |
一開始涕烧,這是一個(gè)空表月而。每當(dāng)掃描一個(gè)區(qū)塊的所有交易后,某些地址的余額增加议纯,另一些地址的余額減少父款,兩者之差恰好為區(qū)塊獎(jiǎng)勵(lì):
address | balance | lastUpdatedAtBlock |
---|---|---|
address-1 | 50.0 | 0 |
address-2 | 40.0 | 3 |
address-3 | 50.0 | 3 |
address-4 | 10.0 | 3 |
這樣,掃描完所有區(qū)塊后瞻凤,我們就得到了整個(gè)區(qū)塊鏈所有地址的完整余額記錄憨攒,查詢的時(shí)候,并不是從區(qū)塊鏈查詢阀参,而是從本地?cái)?shù)據(jù)庫查詢肝集。大多數(shù)錢包程序使用LevelDB來存儲(chǔ)這些信息,手機(jī)錢包程序則是請(qǐng)求服務(wù)器蛛壳,由服務(wù)器查詢數(shù)據(jù)庫后返回結(jié)果杏瞻。
如果我們把MySQL這樣的數(shù)據(jù)庫看作可修改的,那么區(qū)塊鏈就是不可修改炕吸,只能追加的只讀數(shù)據(jù)庫伐憾。但是,MySQL這樣的數(shù)據(jù)庫雖然其狀態(tài)是可修改的赫模,但它的狀態(tài)改變卻是由修改語句(INSERT/UPDATE/DELETE)引起的树肃。把MySQL的binlog日志完整地記錄下來,再進(jìn)行重放瀑罗,即可在另一臺(tái)機(jī)器上完整地重建整個(gè)數(shù)據(jù)庫胸嘴。把區(qū)塊鏈看作不可修改的binlog日志雏掠,我們只要把每個(gè)區(qū)塊的所有交易重放一遍,即可重建一個(gè)地址-余額的數(shù)據(jù)庫劣像。
可見乡话,比特幣的區(qū)塊鏈記錄的是修改日志,而不是當(dāng)前狀態(tài)耳奕。
小結(jié)
比特幣區(qū)塊鏈?zhǔn)褂肬TXO模型绑青,它沒有賬戶這個(gè)概念;
重建整個(gè)地址-余額數(shù)據(jù)庫需要掃描整個(gè)區(qū)塊鏈屋群,并按每個(gè)交易依次更新記錄闸婴,即可得到當(dāng)前狀態(tài)。