【比特幣錢包開發 八】比特幣轉賬交易與交易記錄
作者:ChainDesk李旭,ChainDesk區塊鏈行業分析師,ChainDesk區塊鏈工程師
課程目標
理解交易的輸入、輸出、UTXO
實現比特幣轉賬交易
查詢交易記錄
前言
現在我們的錢包應用程式還差最後一個關鍵的功能:轉賬交易,這就是本章的內容。
根據API文件可見,完整的傳送交易過程需要經過如下四個步驟:
createTxProposal():建立交易。
publishTxProposal():釋出交易。
signTxProposal():簽名交易。
broadcastTxProposal():廣播交易。
一、建立交易的API文件說明
首先我們需要建立一個交易,API文件說明如下:
第一個引數是opts有很多可選引數,第二個引數是Callback回撥。下面解釋下opts的重要引數。
opts.outputs: Array,必填:交易的輸出,即收款方。資料型別是陣列,可見一次交易支援多個轉賬,陣列的元素包含如下:
opts.outputs[].toAddress: String,必填:收款方地址。
opts.outputs[].amount: Number,必填:轉賬數額。
opts.outputs[].message: String,必填:備註訊息。
opts.message: string,必填:本次交易的備註訊息,與opts.outputs[].message不同,它是指定的單個轉賬的備註資訊。
opts.fee: string,可選:本次交易的費用,與feePerKb互斥。
opts.feePerKb: string,可選:本次交易每KB的費用,與fee互斥。
opts.changeAddress: string,可選:使用這個地址作為本次交易的地址,該地址必須屬於該錢包。
opts.payProUrl: String,可選:忽略。
opts.excludeUnconfirmedUtxos: string,可選:不使用未確認事務的UTXOS作為輸入。
opts.customData: Object,可選: 忽略。
opts.inputs: Array,可選:本次交易中使用的輸入。
opts.utxosToExclude: Array,可選:忽略。
二、編碼實現建立交易
我們先傳遞必填引數實現建立交易,程式碼如下:
輸出如下:
輸出資訊有幾個關鍵點:
status:"temporary",臨時狀態。
fee:243,手續費為243Satoshi。
feeLevel:"normal",費用等級是正常。
重點資料如下:
changeAddress:{address:"n1jK5uzvLqJKatSub4dscJ93wsSnvVc6U6",path:"m/1/1"}
inputs:{address: "n3wRnSdhWJtckzMayhJQstCWunpg91kDMs",path:"m/1/0"}
outputs:{toAddress: "mm16s7xsf8Wjwxhprc6YzLW9gVncqZNGBR"}
現在這個交易並沒有傳送出去,要完成這個交易還需進行三個步驟。
三、傳送交易的完整過程
前面已經說到了傳送交易需要四個過程,createTxProposal --> publishTxProposal --> signTxProposal --> broadcastTxProposal。重點是在第一步建立交易createTxProposal,這裡指定本次交易的詳細資料,後面的三部只需執行即可。
輸出內容太多,下面說下重點資料。
每次交易都會改變地址,現在的輸出是
changeAddress:{address:"mi7ZMqMbWfv7p4SHj8iUQXpkuSYbdXe44K",path:"m/1/2"},
上次是"m/1/1"。
inputs:{address: "n3wRnSdhWJtckzMayhJQstCWunpg91kDMs",path:"m/1/0"},
與上次沒有傳送變化,還是"m/1/0",因為上次交易只是建立了並沒有廣播。
outputs:{toAddress: "mm16s7xsf8Wjwxhprc6YzLW9gVncqZNGBR"}
執行完每個步驟對交易都會改變它的狀態
createTxProposal():status:"broadcasted"。
publishTxProposal():status:"pending"。
signTxProposal():status:"accepted"。
broadcastTxProposal():status:"broadcasted"。
成功執行簽名交易後會生成交易id,txid:"57fb33c1199d6e98ae245dc422835d012ed3cab1314e245ca3605de3b44884b4"。
id:"beb003f8-f7d6-4578-a507-c36319a9fc96"。
若幾個步驟沒有傳送錯誤,則交易成功完成。兩個錢包的餘額變化情況是
“wallet1”:0.11121494 BTC-->0.10121251 BTC,差值是0.01000243。
收款方“mm16s7xsf8Wjwxhprc6YzLW9gVncqZNGBR”所在錢包是“importWallet1”。
“importWallet1”:0.011 BTC-->0.021 BTC,差值是0.01。
多扣掉的0.01000243-0.01=0.00000243BTC正是本次交易的手續費,為243Satoshi。
四、查詢交易記錄
現在我們對剛才進行的轉賬看下是否能成功查詢。需要使用getTxHistory()方法,注意,一定要加上引數includeExtendedInfo設定為true,會顯示額外的交易詳情,如:輸入、輸出等。
1. 最近一次轉賬的交易記錄
現在該錢包有三次交易記錄,我將上面進行轉賬的交易記錄詳細截圖如下:
我們先來分析上面出現的三個地址。
inputs:{address: "n3wRnSdhWJtckzMayhJQstCWunpg91kDMs"},
這是本次交易的輸入,通過getBalance()方法的byAddress欄位可以檢視錢包的餘額由哪些子地址擁有。
outputs[0]:{toAddress: "mm16s7xsf8Wjwxhprc6YzLW9gVncqZNGBR"}
這是輸出,是收款方地址。
outputs[1]:{address:"mi7ZMqMbWfv7p4SHj8iUQXpkuSYbdXe44K",path:"m/1/2"}
我們給一個地址轉賬為什麼會出現兩個輸出呢?因為會將輸入裡的餘額取出來一部分進行轉賬,剩餘的錢就轉移到了這個地址,這個地址是該錢包的另外一個新的子賬號地址。所以轉賬之前在"m/1/0"路徑的“n3wR......kDMs”地址中,在本次轉賬完成後就轉移到了"m/1/2"路徑的“mi7Z……e44K”地址。
2. 最初接收的交易記錄
現在再來看看接收轉賬的輸出,在最初索取BTC測試幣的交易就是接收。
可見自己路徑“m/0/0”的地址“move......1KQz”作為了輸出,用於接收交易。同時可以看到有個狀態的欄位
action:"send",代表傳送。
action:"received",代表接收。
3. 第二次交易記錄
在這次交易記錄中將關聯第一次與第三次交易的交易地址。
第一次接收交易的輸出:“move......1KQz”。輸出即擁有餘額的地址。
第二次轉出交易的輸入:“move......1KQz”,輸出(即改變後的地址):“n3wR......kDMs”。
第三次轉出交易的輸入:“n3wR......kDMs”。
其它型別的轉賬自己在測試看一下結果。
4. 結論
比特幣採用的是 UTXO 模型,並非賬戶模型,並不直接存在“餘額”這個概念,獲取餘額需要通過遍歷整個交易歷史獲取。
UTXO:是unspend transaction output的簡寫,指未被花費的交易輸出。
場景:假設你過去分別向A、B、C這三個比特幣使用者購買了BTC,從A手中購買了3.5個BTC,從B手中購買了4.5個BTC,從C手中購買了2個BTC,現在你的比特幣錢包裡面恰好剩餘10個BTC。
問題:這個10個BTC是真正的10個BTC嗎?其實不是,這句話可能聽起來有點怪。(什麼!我錢包裡面的BTC不是真正的BTC,你不要嚇我……)
解釋:前面提到過在比特幣的交易系統當中,並不存在賬戶、餘額這些概念,所以,你的錢包裡面的10個BTC,並不是說錢包餘額為10個BTC。而是說,這10個BTC其實是由你的比特幣地址(錢包地址|公鑰)鎖定了的散落在各個區塊和各個交易裡面的UTXO的總和。
UTXO 是比特幣交易的基本單位,每筆交易都會產生UTXO,一個UTXO可以是一“聰”的任意倍。給某人傳送比特幣實際上是創造新的UTXO,繫結到那個人的錢包地址,並且能被他用於新的支付。
一般的比特幣交易由 交易輸入 和 交易輸出 兩部分組成。A向你支付3.5個BTC這筆交易,實際上產生了一個新的UTXO,這個新的UTXO 等於 3.5個BTC(3.5億聰),並且鎖定到了你的比特幣錢包地址上。
假如你要給你女(男)朋友轉 1.5 BTC,那麼你的錢包會從可用的UTXO中選取一個或多個可用的個體來拼湊出一個大於或等於一筆交易所需的比特幣量。比如在這個假設場景裡面,你的錢包會選取你和C的交易中的UTXO作為 交易輸入,input = 2BTC,這裡會生成兩個新的交易輸出,一個輸出(UTXO = 1.5 BTC)會被繫結到你女(男)朋友的錢包地址上,另一個輸出(UTXO = 0.5 BTC)會作為找零,重新繫結到你的錢包地址上。
我們需要找到所有未花費的交易輸出(UTXO)。Unspent(未花費) 意味著這些交易輸出從未被交易輸入所指向。
五、完整原始碼
1. controllers/transaction.js
controllers資料夾下新建transaction.js檔案,實現比特幣轉賬交易和查詢交易記錄功能。
2. controllers/web.js
編輯controllers資料夾下的web.js檔案,後端實現返回比特幣轉賬交易和查詢交易記錄頁面。
3. router/router.js
將比特幣轉賬交易和查詢交易記錄的介面繫結到路由。
4. static/js/transaction.js
新建transaction.js檔案,處理比特幣轉賬交易的網路請求與介面渲染。
5. views/transaction.html
新建transaction.html檔案,前端顯示比特幣轉賬交易的頁面。
6. static/js/transactionRecord.js
新建transactionRecord.js檔案,處理查詢交易記錄的網路請求與介面渲染。
7. views/transactionRecord.html
新建transactionRecord.html檔案,前端顯示查詢交易記錄的頁面。
六、專案執行效果
轉賬的頁面如下
交易記錄的頁面如下
ChainDesk,是一個學習方式
chaindesk.cn,首創基於區塊鏈的多相腦圖分割模型學習社群
獨創 專業 系統 高效是我們的代名詞,學你想學,想你所想
歡迎加入區塊鏈部落2群,群聊號碼:263270946