本文目的
通過上一篇筆記python-bitcoinrpc下載比特幣數(shù)據(jù)Windows(完成)可以直接通過不同的API獲得解析后的區(qū)塊數(shù)據(jù)或交易記錄,本文分析這些數(shù)據(jù)能給我們提供哪些對分析比特幣網(wǎng)絡(luò)有用的信息锐想。
Block原始數(shù)據(jù)維度
利用bitcoinrpc提供的getblock接口可以幫助我們獲取區(qū)塊數(shù)據(jù)帮寻。該接口需要輸入?yún)^(qū)塊哈希,另外一個(gè)可選變量有三個(gè)取值痛倚,0规婆,1,2。
- 取值為0時(shí)抒蚜,返回原始的16進(jìn)制編碼的數(shù)據(jù)
- 取值為1時(shí)掘鄙,返回解碼后的字典數(shù)據(jù)
- 取值為2時(shí),基本和取值為1時(shí)返回結(jié)果一致嗡髓,只在鍵為tx的值中包含了交易的全數(shù)據(jù)
上圖展示了在pycharm里面運(yùn)行以下代碼獲得的rawdata返回結(jié)果:
from bitcoinrpc.authproxy import AuthServiceProxy
rpc_connection = AuthServiceProxy("http://%s:%s@127.0.0.1:8332"%('xxx', 'xxx'))best_block_hash = rpc_connection.getbestblockhash() #獲取當(dāng)前最長鏈的最后一個(gè)區(qū)塊哈希
rawdata = rpc_connection.getblock(best_block_hash) #獲取指定區(qū)塊哈希的區(qū)塊數(shù)據(jù)
rawdata中包含的數(shù)據(jù)維度有:
# 區(qū)塊數(shù)據(jù)維度
hash #當(dāng)前區(qū)塊的哈希
confirmations #當(dāng)前區(qū)塊已被確認(rèn)數(shù)(即后續(xù)跟著的區(qū)塊數(shù)+1操漠,或者可以直接用當(dāng)前區(qū)塊鏈總高度—當(dāng)前區(qū)塊高度+1計(jì)算獲得),需要注意的是饿这,該數(shù)值會隨著新區(qū)塊的不斷增加而變化浊伙,沒必要作為數(shù)據(jù)維度之一存入數(shù)據(jù)庫
strippedsize #剔除隔離見證數(shù)據(jù)后的區(qū)塊字節(jié)數(shù)
size #區(qū)塊字節(jié)數(shù)
weight #BIP141定義的區(qū)塊權(quán)重
height #當(dāng)前區(qū)塊高度(第一個(gè)區(qū)塊高度為0,于2009-01-04 02:15生成)
version #區(qū)塊版本(代表支持不同的軟长捧、硬分叉方案)
versionHex #用16進(jìn)制表示的區(qū)塊版本
merkleroot #區(qū)塊的默克爾樹根嚣鄙,是根據(jù)當(dāng)前區(qū)塊內(nèi)所有交易哈希生成的默克爾樹的根節(jié)點(diǎn)哈希
tx #當(dāng)前區(qū)塊包含的交易列表,列表中是交易id
time #當(dāng)前區(qū)塊創(chuàng)建的時(shí)間戳(unix timestamp)
mediantime #過去11個(gè)區(qū)塊創(chuàng)建時(shí)間戳的中值(unix timestamp)
nonce #用于挖礦工作量證明的隨機(jī)數(shù)
bits #難度目標(biāo)串结,標(biāo)識了當(dāng)前區(qū)塊頭Hash之后要小于等于的目標(biāo)值(target)
difficulty #區(qū)塊挖礦的難度值
chainwork #當(dāng)前區(qū)塊鏈的工作量加總
nTx #當(dāng)前區(qū)塊包含的交易總數(shù)
previousblockhash #當(dāng)前區(qū)塊鏈接的前序區(qū)塊哈希
nextblockhash #(如有)鏈接當(dāng)前區(qū)塊的后序區(qū)塊哈希
漲知識Tips:
- BIP即Bitcoin Improvement Proposal的首字字母縮寫哑子,意譯為“比特幣改進(jìn)提議”,每個(gè)BIP都牽涉到對比特幣源碼的改動肌割。而BIP僅僅是提議卧蜓,實(shí)施與否需要社區(qū)達(dá)成共識。
- BIP141定義了當(dāng)前的比特幣隔離見證激活方案把敞,大概方法是保存區(qū)塊數(shù)據(jù)時(shí)移除比特幣交易過程中的簽名字段弥奸,從而在不擴(kuò)大區(qū)塊大小的情況下實(shí)現(xiàn)“變相擴(kuò)容”。
- 區(qū)塊頭里面的默克爾樹根是根據(jù)nTx筆交易的哈希作為葉子節(jié)點(diǎn)生成的默克爾樹的根哈希奋早,可參考這篇文章比特幣區(qū)塊結(jié)構(gòu)Merkle樹及簡單支付驗(yàn)證分析中的示意圖理解盛霎。
- unix時(shí)間戳是從1970年1月1日(UTC/GMT的午夜)開始所經(jīng)過的秒數(shù),不考慮閏秒伸蚯。
- mediantime用于確認(rèn)區(qū)塊:只有當(dāng)某個(gè)區(qū)塊的時(shí)間戳大于mediantime摩渺,并且小于某個(gè)調(diào)整后的網(wǎng)絡(luò)時(shí)間(p2p網(wǎng)絡(luò)節(jié)點(diǎn)報(bào)時(shí)中值+2小時(shí)?)時(shí)剂邮,這個(gè)區(qū)塊才會被確認(rèn)。
上面理清了區(qū)塊數(shù)據(jù)的維度横侦,其中有助于分析比特幣交易網(wǎng)絡(luò)的維度主要有:
- hash(可作為區(qū)塊的唯一查詢標(biāo)識)
- height(可作為區(qū)塊的唯一查詢標(biāo)識并用于排序)
- tx(最重要的交易數(shù)據(jù))
- time(時(shí)間戳)
- nTx (當(dāng)前區(qū)塊包含的交易總數(shù))
獲取交易數(shù)據(jù)
獲取比特幣交易數(shù)據(jù)可以通過兩種途徑:
- 使用上述getblock接口并將verbosity設(shè)置為2挥萌,可獲得該區(qū)塊內(nèi)所有交易的詳細(xì)數(shù)據(jù)
rawdata = rpc_connection.getblock(block_hash, 2)
txlist = rawdata['tx']
for txdata in txlist:
...
-
使用getrawtransaction接口并將verbose設(shè)置為True:
getrawtransaction.png
txdata = rpc_connection.getrawtransaction(tx_id,True)
這兩種獲得交易數(shù)據(jù)的方法都能返回一致的結(jié)果,下圖是返回結(jié)果對比枉侧,第一種方式適用于一次性獲取某一個(gè)區(qū)塊里的交易列表引瀑;第二種方式適用于查詢某個(gè)已知txid的特定交易數(shù)據(jù),當(dāng)然榨馁,在單獨(dú)查詢交易數(shù)據(jù)時(shí)也會返回交易所屬區(qū)塊的信息(黃框所示)
交易數(shù)據(jù)維度
從返回的交易數(shù)據(jù),我們可以獲得以下維度的數(shù)據(jù):
# 交易數(shù)據(jù)維度
txid #交易id屑柔,和所要查詢的交易id一致
hash #交易哈希屡萤,對采用隔離驗(yàn)證(segwit)的交易生成的哈希,與txid不同掸宛,若交易未采用隔離驗(yàn)證死陆,則與txid一致
version #版本
size #交易數(shù)據(jù)占據(jù)的字節(jié)數(shù)
vsize #隔離驗(yàn)證后交易數(shù)據(jù)占據(jù)的字節(jié)數(shù)(總是小于size)
weight #BIP141定義的方法計(jì)算占據(jù)權(quán)重,是vsize的4倍取整
locktime #該交易能被加入到區(qū)塊內(nèi)的最早時(shí)間
vin #交易的發(fā)送地址列表
vout #交易的接收地址列表
hex #txid16進(jìn)制編碼后的序列
漲知識Tips:
- 每個(gè)交易都可有多個(gè)發(fā)送地址和多個(gè)接收地址唧瘾,每個(gè)發(fā)送地址都會附帶一個(gè)由發(fā)送地址持有人秘鑰生成的scriptSig措译,即unlocking script,用于解鎖該地址的余額饰序,這個(gè)scriptSig在密碼學(xué)上被稱為“witness”领虹。
- 交易的txid,是對交易信息進(jìn)行兩次sha256的結(jié)果求豫。
- 打包交易的節(jié)點(diǎn)有可乘之機(jī)掠械,作惡節(jié)點(diǎn)可以通過對發(fā)送者提供的witness進(jìn)行包裝生成一個(gè)新的txid,而驗(yàn)證節(jié)點(diǎn)驗(yàn)證整個(gè)交易時(shí)察覺不出txid被篡改注祖。這樣作惡節(jié)點(diǎn)就可以欺騙發(fā)送者交易未成功發(fā)送猾蒂,但實(shí)際上交易已經(jīng)以另一個(gè)txid上鏈了,導(dǎo)致發(fā)送者掉入重復(fù)支付陷阱是晨。
- BIP141建議的隔離驗(yàn)證肚菠,是將witness從交易數(shù)據(jù)中分離出來,放在區(qū)塊底部罩缴,這樣按原來方式算txid就不包括驗(yàn)證信息了蚊逢,而包括驗(yàn)證信息生成的即為hash:
txID: [nVersion][txins][txouts][nLockTime]
hash: [nVersion][marker][flag][txins][txouts][witness][nLockTime]
這就是交易數(shù)據(jù)里面txid和hash有可能不一樣的原因。- 有關(guān)segregated witness如何擴(kuò)容的解釋可參考這篇Medium文章
因?yàn)樵趖xin引用前序交易時(shí)都是指向前序交易的txid箫章,故在保存交易數(shù)據(jù)時(shí)以txid作為其唯一標(biāo)識比較方便烙荷。其他維度中對分析比特幣交易網(wǎng)絡(luò)比較有用的就是vin和vout了,在下面詳細(xì)展開檬寂。
vin和vout包含的內(nèi)容
每一個(gè)區(qū)塊可能包含多筆交易终抽,但第一筆交易都是系統(tǒng)直接向礦工發(fā)送,金額是當(dāng)下挖礦獎勵(lì)+該區(qū)塊內(nèi)的手續(xù)費(fèi)加總桶至。數(shù)據(jù)格式如下:
vin=[{"coinbase": "xxxxxx很長的一串字符串xxxxxx",
"sequence": 0}]
vout=[{"value": Decimal("12.64059504"),
"n": 0昼伴,
"scriptPubKey": {"asm": "xxx",
"hex": "xxx",
"reqSigs": 1,
"type": "pubkeyhash",
"addresses": ["xxx"]}
},
{"value": Decimal("0E-8"),
"n": 1,
"scriptPubKey": {"asm": "xxx",
"hex": "xxx",
"type": "nulldata"}
},
...]
vin是發(fā)送方地址列表,如果一個(gè)vin列表中只有一個(gè)字典镣屹,并且其中存在鍵“coinbase”圃郊,就可以判斷該筆交易是礦工獎勵(lì)。
vout是接收方地址列表女蜈,可能有一至多個(gè)接收方持舆,每個(gè)接收方用一個(gè)字典表示色瘩,一般包含:
value #接收到的比特幣數(shù)額,八位小數(shù)
n #接收方的排序號逸寓,從0開始居兆,便于后續(xù)花費(fèi)時(shí)引用該筆txid,并指明是第n筆
scriptPubKey #接收方信息
- asm #用于分析scriptPubKey的腳本席覆,不同type對應(yīng)的asm不同
- hex #asm的16進(jìn)制編碼
- type #接收方地址類型史辙,可以是pubkeyhash, scripthash, witness_v0_scripthash, multisig, nulldata等
- reqSigs #要求的簽名數(shù)
- addresses #接收方地址,是個(gè)列表佩伤,多重簽名時(shí)可能會有多個(gè)地址
實(shí)際上聊倔,vout的接收方可有多種類型,具體可參考An Analysis of Non-standard Transactions這篇論文【1】生巡,里面詳細(xì)介紹了標(biāo)準(zhǔn)交易類型和非標(biāo)準(zhǔn)交易類型耙蔑。
標(biāo)準(zhǔn)交易類型(Standard Type):
- P2PKH(比特幣網(wǎng)絡(luò)默認(rèn)類型)
- P2PK (多出現(xiàn)在礦工接收獎勵(lì)和手續(xù)費(fèi)的coinbase交易中)
- Multi-signature (多簽)
- P2SH(將另一筆交易的script哈希后作為新script,壓縮字節(jié))
- P2WPKH孤荣、P2WSH:隔離見證采用后P2PKH的另一種實(shí)現(xiàn)方式甸陌,字節(jié)占用更少,且簽名從unlocking script中移出
- nulldata (OP_RETURN): 隔離見證采用后用于存儲見證信息的默克爾樹根( Merkle root of the witness tree)盐股,在coinbase交易的接收方出現(xiàn)钱豁,該筆output將直接從UTXO中移除,后續(xù)不能被花費(fèi)疯汁,轉(zhuǎn)移的比特幣數(shù)量一般都為零牲尺。vout中沒有reqSigs和addresses這兩對鍵值。
論文【1】及Bitcoin Wiki對Transaction的描述信息中介紹了幾種交易類型的腳本形式(asm)幌蚊,例如:
# Pay-to-PubkeyHash(P2PKH)
scriptPubKey: OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
scriptSig: <sig> <pubKey>
其中scriptPubKey是locking script, 保存在vouts里谤碳,scriptSig是發(fā)送方用于解鎖發(fā)送地址中比特幣的簽名信息(unlocking script),在非coinbase交易中的vin出現(xiàn):
vin=[{"txid": "xxx",
"vout": 0,
"scriptSig": {"asm": "xxx",
"hex": "xxx"},
"sequence": 4294967295},
...]
注意:非coinbase交易的vin沒有直接給出發(fā)送方地址信息溢豆,而是告訴我們該筆交易花費(fèi)的前序交易“txid”及“vout”蜒简,“vout”是某一筆花費(fèi)在該前序交易中的接收方排序,即前面分析的vout中的“n”對應(yīng)的值漩仙。通過這個(gè)信息需要再去查詢指定“txid”的第“vout”個(gè)接收方地址搓茬,才能獲得該筆交易的發(fā)送方地址。另外一個(gè)數(shù)據(jù)維度讯赏,sequence垮兑,是不經(jīng)常用到的一個(gè)序列號,用于更改帶時(shí)間鎖的交易漱挎,詳細(xì)可參考官方說明:locktime-and-sequence-number。
原則上雀哨,節(jié)點(diǎn)驗(yàn)證程序會通過isStandard()及isStandardTx()兩個(gè)函數(shù)分別驗(yàn)證所有outputs和inputs是否采用標(biāo)準(zhǔn)交易形式磕谅。但一些礦工節(jié)點(diǎn)修改了程序私爷,導(dǎo)致區(qū)塊里也會打包一些非標(biāo)準(zhǔn)交易,但它們所占的比例很少(0.02%)膊夹。
非標(biāo)準(zhǔn)交易類型(Nonstandard Type):
- 疑似DDOS攻擊:例如Transaction: 2a0597e665ac3d1cabeede95cedf907934db7f639e477b3c77b242140d8cf728在scriptPubKey中寫入上百個(gè)OP_CHECKSIG(消耗驗(yàn)證節(jié)點(diǎn)的計(jì)算量)衬浑,該筆output可以被后續(xù)花費(fèi)。
- P2PKH NOP: 在P2PKH的script形式上末尾加了OP_NOP放刨,用于測試未來的某些新功能工秩,例如Transaction: db3f14e43fecc80eb3e0827cecce85b3499654694d12272bf91b1b2b8c33b5cb,該筆交易可以被后續(xù)花費(fèi)进统。
- P2PKH 0: script形式與P2PKH一樣助币,但是在public key hash的位置填寫的是0,由于0不是有效公鑰哈希螟碎,導(dǎo)致該筆output無法被后續(xù)花費(fèi)眉菱,也沒有接收方地址,轉(zhuǎn)移的金額相當(dāng)于銷毀了掉分。
總結(jié)
本文分析了JSON-bitcoinrpc的若干接口俭缓,
- 通過getblockhash()接口,可獲得指定height的區(qū)塊hash酥郭;
- 通過getblock()接口华坦,可獲得指定區(qū)塊hash的區(qū)塊數(shù)據(jù),設(shè)定第二個(gè)參數(shù)為2可獲得全量交易數(shù)據(jù)列表不从;
- 通過getrawtransaction()接口惜姐,可獲得單個(gè)指定txid的交易數(shù)據(jù);
- 通過getbestblockhash()接口消返,可獲得當(dāng)前最長鏈最后一個(gè)被確認(rèn)區(qū)塊的hash载弄。
另,下面代碼示例可實(shí)現(xiàn)批量獲取返回結(jié)果:
commands1 = [["getblockhash", height] for height in range(100)]
block_hashes = rpc_connection.batch_(commands1)
commands2 = [["getblock", h] for h in block_hashes]
blocks = rpc_connections.batch_(commands2)
對分析比特幣交易網(wǎng)絡(luò)有用的信息主要就是:
- 區(qū)塊相關(guān)的數(shù)據(jù)
- hash(可作為區(qū)塊的唯一查詢標(biāo)識)
- height(可作為區(qū)塊的唯一查詢標(biāo)識并用于排序)
- time(時(shí)間戳)
- nTx (當(dāng)前區(qū)塊包含的交易總數(shù))
- 交易相關(guān)的數(shù)據(jù)
- txid
- vin撵颊,判斷是否coinbase宇攻,引用的前序交易txid及排序號vout
- vout, 接收方列表(每個(gè)接收方包括value, 排序號n, scriptPubKey中的type, reqSigs, addresses)
- blockhash 交易所屬區(qū)塊的哈希
- 派生數(shù)據(jù)
- 手續(xù)費(fèi),發(fā)送方總金額—接收方總金額的差值為支付給礦工的手續(xù)費(fèi)
- 找零倡勇,推斷找零地址及找零余額
- multi-input heuristic
- 通過hash和txid是否一致判斷該交易是否采用隔離見證
由于數(shù)據(jù)量龐大逞刷,如果要存在數(shù)據(jù)庫中,下一步是設(shè)計(jì)數(shù)據(jù)倉庫結(jié)構(gòu)妻熊,以便能夠有效地查詢到所需數(shù)據(jù)夸浅。
(記于2019年12月31日 漠水云)