費(fèi)機(jī)制精讀)
讀《精通比特幣》第3版讀到第6章交易這一章才真正摸到了比特幣的內(nèi)核。前面幾章講的密鑰、地址、錢(qián)包本質(zhì)上都是在為交易做鋪墊——地址是交易的收件箱私鑰是交易的簽名筆而交易本身才是比特幣賬本上唯一可寫(xiě)入的數(shù)據(jù)。我見(jiàn)過(guò)不少朋友跳過(guò)這一章直接去查區(qū)塊瀏覽器后來(lái)做鏈上量化分析時(shí)才發(fā)現(xiàn)連找零和真實(shí)轉(zhuǎn)賬都分不清統(tǒng)計(jì)口徑自然一團(tuán)糟。這一章最厲害的地方是用極其扎實(shí)的細(xì)節(jié)把交易的整個(gè)生命周期、內(nèi)部結(jié)構(gòu)、腳本和手續(xù)費(fèi)機(jī)制講透了。讀完這一章你至少能回答三件事一筆交易從構(gòu)想到上鏈經(jīng)歷了什么交易里的輸入輸出為什么不能理解成賬戶(hù)轉(zhuǎn)賬手續(xù)費(fèi)到底是怎么算出來(lái)的。無(wú)論你是做錢(qián)包開(kāi)發(fā)、節(jié)點(diǎn)運(yùn)維還是時(shí)下很多人關(guān)注的比特幣量化這章都是繞不過(guò)去的地基。接下來(lái)我按自己的閱讀節(jié)奏把這章的核心拆開(kāi)講一遍順便聊一些實(shí)際操作中容易踩的坑。1. 交易章節(jié)為什么是整本書(shū)的樞紐1.1 區(qū)塊只是容器交易才是血液很多人看比特幣第一反應(yīng)是看區(qū)塊好像區(qū)塊鏈就是一長(zhǎng)串塊。但打開(kāi)一個(gè)區(qū)塊看里面裝的其實(shí)就是一組交易。所謂賬本并沒(méi)有一張賬戶(hù)表這個(gè)地址有多少錢(qián)指的是這個(gè)地址在歷史交易輸出中能支配多少UTXO。所以交易才是比特幣真正流通的血液礦工打包區(qū)塊本質(zhì)也是在從內(nèi)存池里挑選合法交易。我自己早期做開(kāi)發(fā)時(shí)一直以為錢(qián)包余額是節(jié)點(diǎn)幫忙維護(hù)的賬戶(hù)數(shù)據(jù)后來(lái)才發(fā)現(xiàn)錢(qián)包只是掃描屬于自己的UTXO把金額加起來(lái)匯總。這個(gè)理解在第6章里講得非常清楚屬于看完整章才會(huì)徹底轉(zhuǎn)過(guò)來(lái)的彎。1.2 承上啟下的位置第6章正好夾在密鑰、錢(qián)包和腳本、共識(shí)之間。拿它和第4章連起來(lái)看你會(huì)理解地址背后其實(shí)是一把鎖定腳本拿它和第7章連起來(lái)看你會(huì)發(fā)現(xiàn)標(biāo)準(zhǔn)腳本只是拋磚引玉現(xiàn)代腳本體系要復(fù)雜得多拿它和第10章連起來(lái)看交易的規(guī)則就是共識(shí)的一部分改變交易結(jié)構(gòu)就等于改變共識(shí)。所以這一章不讀透后面每一章都會(huì)覺(jué)得缺了一塊。做比特幣量化也是同理。你在交易所或鏈上拉到的每一行K線、每一筆成交最終源頭都是鏈上交易。如果不理解交易的結(jié)構(gòu)、字段語(yǔ)義和確認(rèn)邊界你在跑統(tǒng)計(jì)模型時(shí)很容易把已經(jīng)被RBF替換掉的舊交易也當(dāng)成實(shí)際轉(zhuǎn)賬或者把找零地址當(dāng)成活躍用戶(hù)。這個(gè)道理第6章讀完之后自然就明白了。2. 交易生命周期一筆錢(qián)是怎么走起來(lái)的2.1 構(gòu)造交易的三個(gè)基本動(dòng)作一筆交易不是憑空產(chǎn)生的它通常由錢(qián)包完成三個(gè)動(dòng)作。第一步是選擇UTXO作為輸入。錢(qián)包會(huì)掃一遍自己控制的UTXO集合挑出金額足夠的若干UTXO湊出需要花費(fèi)的總額加手續(xù)費(fèi)。第二步是構(gòu)建輸出。給收款地址指定一個(gè)金額給自己找零地址寫(xiě)回剩余的金額多出來(lái)的差額就是手續(xù)費(fèi)。第三步是簽名。對(duì)交易內(nèi)容做哈希摘要用私鑰簽上然后把解鎖數(shù)據(jù)和交易一起組裝起來(lái)。這里要注意一個(gè)看似反直覺(jué)的點(diǎn)一筆交易有多個(gè)輸入時(shí)所有輸入都必須簽名解鎖。如果其中一個(gè)輸入的腳本驗(yàn)證失敗整個(gè)交易就是非法的。就像你花錢(qián)時(shí)不可能拿一張真鈔配一張假鈔交易是整體有效或整體無(wú)效的。2.2 廣播、內(nèi)存池和確認(rèn)構(gòu)造好的交易要廣播給節(jié)點(diǎn)。節(jié)點(diǎn)收到后第一件事是驗(yàn)證結(jié)構(gòu)、簽名、引用的UTXO是否未被花費(fèi)。驗(yàn)證通過(guò)就放進(jìn)本地內(nèi)存池同時(shí)繼續(xù)轉(zhuǎn)發(fā)給相鄰節(jié)點(diǎn)。此時(shí)交易是0確認(rèn)狀態(tài)。礦工從內(nèi)存池里挑交易打包成候選區(qū)塊挖出后廣播新區(qū)塊這筆交易才算獲得第1個(gè)確認(rèn)。每個(gè)新區(qū)塊加在它上面確認(rèn)數(shù)就加一。需要特別注意的是內(nèi)存池是節(jié)點(diǎn)本地的不同節(jié)點(diǎn)的內(nèi)存池內(nèi)容可能不一樣。你在一臺(tái)機(jī)器上看到一個(gè)待確認(rèn)交易另一臺(tái)機(jī)器可能根本不知道它。這也是為什么一些基礎(chǔ)設(shè)施服務(wù)商會(huì)維護(hù)全網(wǎng)的內(nèi)存池快照做量化分析時(shí)不能把本地內(nèi)存池里有多少筆交易當(dāng)作全局事實(shí)。2.3 雙重支付怎么被攔住雙重支付說(shuō)白了就是同一筆錢(qián)想花兩次。由于每筆輸入必須引用特定的一筆未花費(fèi)輸出一旦某個(gè)UTXO被某筆交易引用其他合法交易就不能再引用它。第一個(gè)進(jìn)入內(nèi)存池的交易會(huì)被節(jié)點(diǎn)接受第二個(gè)引用同一UTXO的交易會(huì)被當(dāng)成沖突交易拒絕除非第一筆被RBF替換或超時(shí)過(guò)期。最終礦工打包時(shí)也只能選其中一個(gè)另一筆會(huì)被丟出內(nèi)存池。這個(gè)機(jī)制跟現(xiàn)金完全不同。現(xiàn)金是同一張鈔票可以反復(fù)花UTXO模式是一張票撕掉就沒(méi)了。你花0.3 BTC時(shí)錢(qián)包會(huì)把一個(gè)0.5 BTC的UTXO整個(gè)花掉拆成0.3給收款方、0.2找回自己。這種整進(jìn)整出的模式如果不精讀第6章一開(kāi)始真不容易拐過(guò)彎來(lái)。3. 解剖交易結(jié)構(gòu)輸入輸出是怎么組織的3.1 一張完整的交易字段表交易數(shù)據(jù)可以看成幾個(gè)連續(xù)拼接的字段。最前面是版本號(hào)然后是輸入數(shù)量、輸入列表、輸出數(shù)量、輸出列表最后是鎖定時(shí)間。網(wǎng)上很多資料喜歡畫(huà)大圖其實(shí)用一張表看更清晰字段長(zhǎng)度作用version4字節(jié)版本號(hào)當(dāng)前普遍為2input count1或更多字節(jié)輸入數(shù)量用可變長(zhǎng)整數(shù)編碼inputs不定每個(gè)輸入包含txid、vout、scriptSig、sequenceoutput count1或更多字節(jié)輸出數(shù)量outputs不定每個(gè)輸出包含金額、鎖定腳本locktime4字節(jié)最早可被打包的區(qū)塊高度或時(shí)間每個(gè)輸入里txid占32字節(jié)vout占4字節(jié)解鎖腳本是變長(zhǎng)的sequence占4字節(jié)。輸出則是8字節(jié)金額加變長(zhǎng)鎖定腳本。所以交易體積大多被輸入引用和腳本占掉。搞清楚這個(gè)表后面解析原始交易數(shù)據(jù)時(shí)就不會(huì)發(fā)怵。3.2 UTXO概念再展開(kāi)UTXO全稱(chēng)是Unspent Transaction Output未花費(fèi)交易輸出。你可以把它想成一張張記錄在鏈上的支票。某筆交易給地址A寫(xiě)了一張0.5 BTC的支票這筆錢(qián)的記錄就是那個(gè)輸出的金額和鎖定腳本。地址A要花這筆錢(qián)就是出示這張支票并在背面簽名然后把整張支票拆成新面額寫(xiě)給別人。全節(jié)點(diǎn)維護(hù)的UTXO集合就是所有還沒(méi)被花掉的輸出。比特幣的賬本本質(zhì)不是地址余額列表而是這個(gè)集合。區(qū)塊瀏覽器顯示的地址余額是程序掃出該地址相關(guān)UTXO后加總的結(jié)果。這個(gè)區(qū)別極其關(guān)鍵做量化的人如果直接拿地址余額變化來(lái)推資金流向會(huì)發(fā)現(xiàn)對(duì)不上賬原因就在這里。3.3 關(guān)于交易ID的字節(jié)序問(wèn)題交易ID是交易數(shù)據(jù)的double-SHA256哈希但呈現(xiàn)的時(shí)候有個(gè)坑區(qū)塊瀏覽器和RPC里顯示的txid是原始哈希字節(jié)反轉(zhuǎn)后的結(jié)果所以看起來(lái)跟你在代碼里自己算出的哈希是反的。這個(gè)反轉(zhuǎn)約定來(lái)自比特幣特定的序列化格式。很多人第一次對(duì)著區(qū)塊數(shù)據(jù)做校驗(yàn)時(shí)都卡在這里。讀這一章時(shí)我建議你直接拿一筆真實(shí)交易手工算一遍sha256d再把字節(jié)逆序跟瀏覽器的txid對(duì)比。親自動(dòng)手做一次比看十遍文檔都管用。想驗(yàn)證自己算得對(duì)不對(duì)也可以去區(qū)塊瀏覽器找一個(gè)交易把它的hex拉下來(lái)自己hash一下。3.4 SegWit帶來(lái)的ID變化隔離見(jiàn)證之后交易有了兩個(gè)ID不含見(jiàn)證數(shù)據(jù)的txid和包含全部見(jiàn)證數(shù)據(jù)的wtxid。前者不隨簽名變化解決了早期交易延展性問(wèn)題。你看到一筆交易的txid不變但witness變了不代表交易數(shù)據(jù)沒(méi)變只是txid故意不涵蓋這部分。對(duì)做底層開(kāi)發(fā)的人來(lái)說(shuō)區(qū)分txid和wtxid是必修課。另外SegWit交易在序列化去掉了scriptSig簽名挪到見(jiàn)證數(shù)據(jù)里。這個(gè)設(shè)計(jì)的直接收益是交易體積減少因?yàn)橐?jiàn)證數(shù)據(jù)在區(qū)塊里按四分之一權(quán)重計(jì)費(fèi)。后續(xù)我們聊手續(xù)費(fèi)率時(shí)會(huì)再碰這個(gè)點(diǎn)。4. 輸出腳本決定誰(shuí)有權(quán)動(dòng)用這筆錢(qián)4.1 用密碼鎖來(lái)理解腳本每筆交易輸出里有一段鎖定腳本相當(dāng)于給硬幣上了一把密碼鎖。要想花這筆錢(qián)輸入里必須提供對(duì)應(yīng)的鑰匙也就是解鎖腳本或見(jiàn)證數(shù)據(jù)。比特幣腳本執(zhí)行時(shí)先把解鎖腳本壓入棧再執(zhí)行鎖定腳本最后看棧頂是否為真。返回真就能花這筆錢(qián)返回假就無(wú)效。腳本語(yǔ)言不圖靈完備沒(méi)有循環(huán)和跳轉(zhuǎn)是為了保證任何一筆交易的驗(yàn)證都能快速終止。你可以把腳本想象成自動(dòng)售貨機(jī)的投幣流程按鍵是固定的但你把不同操作碼組合起來(lái)可以做出多重簽名、時(shí)間鎖、條件支付等復(fù)雜收款條件。第7章會(huì)展開(kāi)講高級(jí)腳本第6章只需要理解這個(gè)鎖和鑰匙模型就夠了。4.2 常見(jiàn)標(biāo)準(zhǔn)輸出類(lèi)型對(duì)比目前常見(jiàn)的標(biāo)準(zhǔn)輸出類(lèi)型不止五種我整理了一份更完整的對(duì)照類(lèi)型地址形態(tài)鎖定內(nèi)容解鎖條件P2PK早期1開(kāi)頭公鑰對(duì)應(yīng)私鑰簽名P2PKH1開(kāi)頭公鑰哈希簽名加公鑰P2SH3開(kāi)頭腳本哈希簽名加贖回腳本P2WPKHbc1q公鑰哈希簽名加公鑰見(jiàn)證數(shù)據(jù)中P2WSHbc1q腳本哈希簽名加贖回腳本見(jiàn)證數(shù)據(jù)中P2TRbc1p公鑰TaprootSchnorr簽名從P2PKH到P2SH再到SegWit和Taproot本質(zhì)上是不斷把驗(yàn)證邏輯從明文腳本中抽象出去同時(shí)把空間從區(qū)塊數(shù)據(jù)中省出來(lái)。哈希鎖定讓輸入方不需要暴露完整腳本Taproot又進(jìn)一步讓復(fù)雜條件看起來(lái)和普通支付一樣。對(duì)照第4章的地址知識(shí)把這兩章合并起來(lái)看最有效。4.3 Taproot與輸出描述符第3版花了很大篇幅講Taproot。P2TR的優(yōu)勢(shì)在于無(wú)論你是單個(gè)簽名還是復(fù)雜的多簽、時(shí)間鎖腳本鏈上看到的都只是一個(gè)公鑰和一把Schnorr簽名外部沒(méi)人能分辨你的實(shí)際收款條件。這個(gè)隱私提升和交易體積的節(jié)省是同步的Taproot還支持批量簽名聚合多簽場(chǎng)景下能顯著降低手續(xù)費(fèi)。配套出現(xiàn)的還有輸出描述符。以前錢(qián)包備份恢復(fù)靠掃地址但地址類(lèi)型五花八門(mén)不同錢(qián)包對(duì)腳本表述不統(tǒng)一。描述符用一串文本把類(lèi)型、路徑、公鑰或密鑰信息完整描述出來(lái)比如pkh、wpkh、tr這些前綴再配合BIP32派生路徑跨錢(qián)包遷移就穩(wěn)了。第6章雖然講交易但書(shū)中會(huì)把描述符和交易腳本串起來(lái)理解了描述符再看現(xiàn)代錢(qián)包代碼會(huì)輕松很多。5. 手續(xù)費(fèi)那些事如何讓交易被更快確認(rèn)5.1 手續(xù)費(fèi)的本質(zhì)是差值比特幣交易里沒(méi)有專(zhuān)門(mén)的手續(xù)費(fèi)字段。手續(xù)費(fèi)就是輸入總額減去輸出總額的差值。錢(qián)包選擇輸入時(shí)會(huì)把想付的金額加上期望的手續(xù)費(fèi)一起湊數(shù)。比如輸入一個(gè)0.5 BTC的UTXO輸出0.3 BTC給收款人、0.1999 BTC找零回自己那0.0001 BTC就是手續(xù)費(fèi)等于10000聰。在交易數(shù)據(jù)里你看不到fee xxx這樣的字段只能自己減出來(lái)。這個(gè)設(shè)計(jì)很巧妙礦工不需要信任錢(qián)包報(bào)了多少錢(qián)直接從輸入輸出總量就能推導(dǎo)手續(xù)費(fèi)。也正因?yàn)槭掷m(xù)費(fèi)是差值構(gòu)造交易時(shí)如果不留差值交易會(huì)被節(jié)點(diǎn)當(dāng)作輸出超過(guò)輸入而拒絕。有些錢(qián)包報(bào)錯(cuò)說(shuō)交易金額無(wú)效其實(shí)根因就是手續(xù)費(fèi)沒(méi)留夠。5.2 手續(xù)費(fèi)率的計(jì)算礦工排序不是按手續(xù)費(fèi)總額排而是按每vB的費(fèi)率排。SegWit引入了vBytes概念把witness部分按四分之一權(quán)重計(jì)入體積。所以同樣一筆交易SegWit版本比普通版本更便宜這就是為什么現(xiàn)在錢(qián)包普遍優(yōu)先走P2WPKH或P2TR。舉個(gè)例子一筆輸入2 BTC、輸出1.9995 BTC的交易手續(xù)費(fèi)是0.0005 BTC即50000聰。如果交易的vsize是250 vB費(fèi)率就是200 sat/vB。礦工優(yōu)先打包費(fèi)率高的交易當(dāng)內(nèi)存池?fù)矶聲r(shí)費(fèi)率低的交易可能幾小時(shí)甚至幾天都出不去。這個(gè)位次競(jìng)爭(zhēng)每天在內(nèi)存池里反復(fù)上演研究它本身就是很有意思的微觀經(jīng)濟(jì)學(xué)樣本。5.3 手續(xù)費(fèi)太低卡住了怎么辦如果一筆未確認(rèn)交易因費(fèi)率過(guò)低卡在內(nèi)存池有兩個(gè)常見(jiàn)補(bǔ)救思路。RBF是用更高費(fèi)率的同類(lèi)型交易替換原來(lái)的交易前提是錢(qián)包在構(gòu)造交易時(shí)把sequence設(shè)成可替換值。CPFP則是花掉那筆卡住交易的某個(gè)輸出發(fā)起一筆新交易用子交易的高費(fèi)率吸引礦工把父子交易一起打包。這兩個(gè)機(jī)制在書(shū)里都有涉及但我真正想強(qiáng)調(diào)的是產(chǎn)品層面的教訓(xùn)錢(qián)包如果沒(méi)在構(gòu)造交易前給用戶(hù)留好可升級(jí)費(fèi)率的選項(xiàng)一旦網(wǎng)絡(luò)擁堵用戶(hù)就只能干等。等久了用戶(hù)就會(huì)跑掉。手續(xù)費(fèi)設(shè)計(jì)不只是技術(shù)問(wèn)題更是產(chǎn)品體驗(yàn)的第一道門(mén)。6. 鏈上量化視角交易數(shù)據(jù)究竟能告訴我們什么6.1 常見(jiàn)的鏈上觀測(cè)指標(biāo)比特幣量化這個(gè)領(lǐng)域很多人只盯著價(jià)格數(shù)據(jù)其實(shí)鏈上數(shù)據(jù)也是重要輸入。比如活躍地址數(shù)、交易筆數(shù)、手續(xù)費(fèi)總量、大額UTXO移動(dòng)、UTXO年齡分布本質(zhì)上都是對(duì)交易數(shù)據(jù)的聚合統(tǒng)計(jì)。一筆交易從廣播到被打包的時(shí)間差也能刻畫(huà)網(wǎng)絡(luò)擁堵程度。手續(xù)費(fèi)總額是我覺(jué)得最被低估的指標(biāo)。它同時(shí)包含了交易數(shù)量和網(wǎng)絡(luò)規(guī)模的信息手續(xù)費(fèi)總額快速抬升時(shí)通常意味著網(wǎng)絡(luò)活躍度在升高。UTXO年齡分布則能反映長(zhǎng)期持有者是否在移動(dòng)幣——如果大量五年以上的老UTXO開(kāi)始頻繁出現(xiàn)鏈上分析師就會(huì)重點(diǎn)關(guān)注。這些都是第6章交易知識(shí)直接延伸出來(lái)的應(yīng)用。6.2 量化建模前必須先處理好三個(gè)口徑問(wèn)題第一找零和真實(shí)轉(zhuǎn)移要分開(kāi)。找零地址往往有規(guī)律比如金額結(jié)構(gòu)固定、腳本類(lèi)型單一、極少被再花。如果不做找零識(shí)別統(tǒng)計(jì)的凈轉(zhuǎn)賬量會(huì)明顯偏大。第二交易所地址要打標(biāo)簽。很多數(shù)據(jù)服務(wù)商提供地址標(biāo)簽但自己最好也設(shè)一套規(guī)則否則無(wú)法區(qū)分交易所內(nèi)部轉(zhuǎn)賬和真實(shí)的用戶(hù)提取。第三未確認(rèn)交易與重組問(wèn)題。鏈上數(shù)據(jù)不是一成不變的節(jié)點(diǎn)同步時(shí)間差、短分叉、重組都會(huì)造成短期視圖不一致所以數(shù)據(jù)快照的時(shí)間點(diǎn)必須固定。這些算法本身不復(fù)雜難點(diǎn)在于對(duì)交易結(jié)構(gòu)底層的理解。理解了交易的輸入輸出、找零邏輯和確認(rèn)機(jī)制才知道哪些字段可信哪些字段需要二次加工。6.3 量化特征構(gòu)造的一點(diǎn)實(shí)操思路從交易數(shù)據(jù)構(gòu)造特征因子時(shí)我習(xí)慣分三層。第一層是絕對(duì)量比如交易筆數(shù)、活躍地址數(shù)、手續(xù)費(fèi)總量。第二層是比率比如某個(gè)年齡帶UTXO的消費(fèi)占比、大額轉(zhuǎn)賬筆數(shù)占比。第三層是交叉信號(hào)比如價(jià)格走勢(shì)與鏈上活躍度的偏離。每一層建立之前都要先跑一遍數(shù)據(jù)清洗。這里不討論買(mǎi)賣(mài)方向只強(qiáng)調(diào)一點(diǎn)特征算得準(zhǔn)不準(zhǔn)首先取決于你對(duì)交易原始數(shù)據(jù)有沒(méi)有吃透。很多人上來(lái)就用第三方API拉現(xiàn)成指標(biāo)結(jié)果不同的API口徑還不一樣最后只能一個(gè)一個(gè)對(duì)齊。與其這樣不如先把第6章的數(shù)據(jù)結(jié)構(gòu)學(xué)扎實(shí)自己寫(xiě)清洗腳本才能對(duì)數(shù)據(jù)質(zhì)量有底。7. 精讀實(shí)操這些方法讓第6章真正變成自己的7.1 起一個(gè)regtest本地環(huán)境光看書(shū)不實(shí)操交易結(jié)構(gòu)永遠(yuǎn)是抽象的。我建議搭一個(gè)regtest環(huán)境用Bitcoin Core的RPC接口自己走一遍完整流程bitcoind -regtest -daemon bitcoin-cli -regtest createwallet test bitcoin-cli -regtest getnewaddress alice bitcoin-cli -regtest generatetoaddress alice 101先用generatetoaddress挖出101個(gè)區(qū)塊獲得幣再用getnewaddress生成收款地址調(diào)用sendtoaddress發(fā)起轉(zhuǎn)賬最后再用generatetoaddress打包一個(gè)區(qū)塊讓交易被確認(rèn)。整個(gè)過(guò)程不到五分鐘但對(duì)交易如何產(chǎn)生、如何確認(rèn)會(huì)有非常直觀的感受。regtest的好處是出塊秒級(jí)想怎么折騰都行。7.2 用RPC解剖一筆交易有了交易就能用getrawtransaction拿到原始hex再用decoderawtransaction解析字段bitcoin-cli -regtest getrawtransaction txid bitcoin-cli -regtest decoderawtransaction hex把輸出里的vin、vout逐字段對(duì)照第6章看一遍再手動(dòng)算一下輸入總額減輸出總額你會(huì)第一次真切理解手續(xù)費(fèi)是差值。如果想再深入一步可以手動(dòng)構(gòu)造一筆裸交易指定輸入腳本、簽名后再?gòu)V播。那個(gè)過(guò)程比點(diǎn)錢(qián)包按鈕要深刻得多做完一次transaction結(jié)構(gòu)基本就刻進(jìn)腦子里了。7.3 對(duì)照源碼加深理解第6章講到的很多細(xì)節(jié)在Bitcoin Core源碼里有清晰對(duì)應(yīng)。腳本執(zhí)行邏輯看interpreter.cpp里的EvalScript交易合法性檢查看validation.cpp里的CheckTransaction交易數(shù)據(jù)結(jié)構(gòu)看primitives/transaction.h里的CTransaction。不要求全都看懂關(guān)鍵是在讀到一個(gè)概念時(shí)能去源碼里找到一個(gè)位置錨定下來(lái)。我自己的經(jīng)驗(yàn)是很多東西只看書(shū)容易忘但如果你能在regtest上親手把一筆交易從構(gòu)造、廣播到打包走一遍半年后再回來(lái)這些字段的名字和含義都會(huì)記得很牢。再遇到錢(qián)包報(bào)錯(cuò)或者鏈上數(shù)據(jù)對(duì)不上你會(huì)比身邊人多一種排查問(wèn)題的直覺(jué)。8. 常見(jiàn)誤區(qū)與問(wèn)題排查速查8.1 常見(jiàn)問(wèn)題速查表整理了幾個(gè)做開(kāi)發(fā)和分析時(shí)最容易遇上的問(wèn)題一張表說(shuō)清現(xiàn)象典型原因解決思路交易長(zhǎng)時(shí)間0確認(rèn)手續(xù)費(fèi)率過(guò)低、網(wǎng)絡(luò)擁堵等待或啟用RBF/CPFP提高費(fèi)率錢(qián)包余額和瀏覽器對(duì)不上UTXO掃描范圍不全、描述符缺失重新掃描所有腳本類(lèi)型和派生路徑手動(dòng)構(gòu)造交易總驗(yàn)證失敗簽名hash類(lèi)型不對(duì)、腳本類(lèi)型不匹配檢查SIGHASH類(lèi)型和腳本執(zhí)行返回值txid在代碼里與瀏覽器顯示不一致字節(jié)序反轉(zhuǎn)把哈希字節(jié)逆序后再比較自己做鏈上統(tǒng)計(jì)和瀏覽器差異大找零沒(méi)識(shí)別、未確認(rèn)交易統(tǒng)計(jì)不一致固定快照時(shí)間點(diǎn)、加入找零識(shí)別規(guī)則這些問(wèn)題大多不是算法多難而是底層細(xì)節(jié)沒(méi)吃透。排查時(shí)先從第6章的幾個(gè)關(guān)鍵詞入手UTXO、鎖定腳本、sequence、字節(jié)序大多能定位到問(wèn)題所在。8.2 幾個(gè)容易被忽略的細(xì)節(jié)sequence字段在很多交易里就是0xFFFFFFFF很多人覺(jué)得它沒(méi)意義但它和RBF、鎖時(shí)都有關(guān)系。locktime字段也不總是0有些隱私錢(qián)包會(huì)故意設(shè)置它如果你做量化統(tǒng)計(jì)時(shí)不解析這個(gè)字段可能漏掉一類(lèi)特殊交易。另外解析輸入數(shù)據(jù)時(shí)要區(qū)分P2PKH和P2SH的解鎖腳本P2PKH的scriptSig里直接放著公鑰和簽名而P2SH的scriptSig里放的是贖回腳本看長(zhǎng)度就能分辨。還有一個(gè)很容易繞暈的細(xì)節(jié)地址本身并不存在于交易數(shù)據(jù)里交易輸出中存的只是腳本。區(qū)塊瀏覽器把腳本轉(zhuǎn)成地址只是為了顯示方便。你從交易數(shù)據(jù)里解析出來(lái)的地址其實(shí)是你自己把腳本哈希格式化成地址的結(jié)果。這個(gè)邏輯不搞清楚寫(xiě)解析程序時(shí)會(huì)被各種庫(kù)搞得一頭霧水。其實(shí)讀到第6章之前我也覺(jué)得比特幣的核心難點(diǎn)是密碼學(xué)和共識(shí)算法。真的把這一章嚼透之后才發(fā)現(xiàn)日常開(kāi)發(fā)中90%的疑難雜癥最后都?xì)w結(jié)于對(duì)交易結(jié)構(gòu)的理解不到位。無(wú)論是錢(qián)包的UTXO掃描還是鏈上量化里的找零識(shí)別或者是SegWit的vB計(jì)算根子都在這章里。我個(gè)人習(xí)慣是每讀完一章就在regtest上把書(shū)里的例子原樣跑一遍然后寫(xiě)下三個(gè)自己覺(jué)得最容易出錯(cuò)的概念。這一章我的三個(gè)關(guān)鍵詞是UTXO是唯一狀態(tài)、手續(xù)費(fèi)是差值、txid和wtxid是兩個(gè)東西。幾年后再回憶這本書(shū)腦海里剩下的就是這些扎在實(shí)處的概念。也建議你精讀時(shí)不要只畫(huà)線試著用代碼把每一筆交易都拆開(kāi)看一次那才算真的讀懂了。