創(chuàng)世區(qū)塊創(chuàng)建完畢之后,按照我們的正常思路,是繼續(xù)創(chuàng)建新的區(qū)塊,并加入至區(qū)塊鏈中,沒錯,這確實是學(xué)習(xí)路線,但是我們首先來了解一個區(qū)塊是如何生成的,轉(zhuǎn)賬交易 ===>打包交易 ===>工作量證明 ===>生成區(qū)塊
創(chuàng)新互聯(lián)主要從事成都網(wǎng)站設(shè)計、做網(wǎng)站、成都外貿(mào)網(wǎng)站建設(shè)公司、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)昌黎,十余年網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792
在上文,我們提到了錢包地址這個概念,我們一般可以簡單將錢包地址理解為一個銀行賬戶,那么交易也就可以理解為是地址與地址之間的轉(zhuǎn)賬過程。
因為這部分內(nèi)容非常重要,設(shè)置可以說交易就是比特幣原理的核心,所以,為了保證大家對概念有充分的了解,本章節(jié)的理論描述部分此處摘錄liuchengxu中關(guān)于對交易的翻譯。
交易(transaction)是比特幣的核心所在,而區(qū)塊鏈唯一的目的,也正是為了能夠安全可靠地存儲交易。在區(qū)塊鏈中,交易一旦被創(chuàng)建,就沒有任何人能夠再去修改或是刪除它。今天,我們將會開始實現(xiàn)交易。不過,由于交易是很大的話題,我會把它分為兩部分來講:在今天這個部分,我們會實現(xiàn)交易的基本框架。在第二部分,我們會繼續(xù)討論它的一些細(xì)節(jié)。
由于比特幣采用的是 UTXO 模型,并非賬戶模型,并不直接存在“余額”這個概念,余額需要通過遍歷整個交易歷史得來。
關(guān)于UTXO模型,這在比特幣中也是非常重要的概念模型,務(wù)必熟練掌握。
點擊此處查看相關(guān)的交易信息
圖 交易記錄
圖 輸入腳本
關(guān)于轉(zhuǎn)賬交易涉及到的內(nèi)容非常多,由于時間原因,目前可能無法做到非常全面的講解,姑且將自己梳理好能夠解釋清楚的地方分享出來,由于比特幣世界中的交易規(guī)則會更加復(fù)雜化,所以,希望大家能夠通過本章節(jié)的閱讀,在一定程度上對某些概念有一些初步或者稍微深刻的理解,那么本章節(jié)的目的也就達(dá)到了,更深的分析筆者將會在后期的工作中根據(jù)實際的工作場景進(jìn)行優(yōu)化并做相關(guān)記錄。
其實再轉(zhuǎn)賬交易這個功能里面,涉及了本文所有的結(jié)構(gòu)體對象,由于區(qū)塊與區(qū)塊鏈對象等在上文已經(jīng)有所提及,這里先列出跟轉(zhuǎn)賬交易關(guān)系最為密切的一些結(jié)構(gòu)體。
type Transaction struct {
//1.交易ID
TxID []byte
//2.輸入
Vins []*TxInput
//3.輸出
Vouts []*TxOutput
}
type TxInput struct {
//1.交易ID:
TxID []byte
//2.下標(biāo)
Vout int
//3.數(shù)字簽名
Signature []byte
//4.原始公鑰,錢包里的公鑰
PublicKey []byte
}
type TxOutput struct {
//金額
Value int64 //金額
//鎖定腳本,也叫輸出腳本,公鑰,目前先理解為用戶名,鑰花費這筆前,必須鑰先解鎖腳本
//ScriptPubKey string
PubKeyHash [] byte//公鑰哈希
}
Value: 金額,轉(zhuǎn)賬/找零金額
type UTXO struct {
//1.該output所在的交易id
TxID []byte
//2.該output 的下標(biāo)
Index int
//3.output
Output *TxOutput
}
UTXO:Unspent Transaction output
type UTXOSet struct {
BlockChian *BlockChain
}
const utxosettable = "utxoset"
定義一個常量用于標(biāo)識存入數(shù)據(jù)庫中的Bucket表名
單筆轉(zhuǎn)賬
$ ./mybtc send -from 源地址 -to 目標(biāo)地址 -amount 轉(zhuǎn)賬金額
多筆轉(zhuǎn)賬
$ ./mybtc send \
-from '["源地址1","源地址2","源地址N"]' \
-to '["目標(biāo)地址1","目標(biāo)地址2","目標(biāo)地址3"]' \
-amount '["轉(zhuǎn)賬金額1","轉(zhuǎn)賬金額2","轉(zhuǎn)賬金額3"]'
這部分內(nèi)容理解起來有些難度,所以我做了一張圖,希望能夠幫助大家能夠理順?biāo)悸?,這樣在后面的學(xué)習(xí)以及代碼理解上面會稍微容易一些。
本圖介紹了從創(chuàng)世區(qū)塊后的三次轉(zhuǎn)賬過程,分別產(chǎn)生了三個區(qū)塊,為了讓讀者有更直觀的了解,我又將該圖做成了動態(tài)圖的方式供大家參考,通過該圖,希望大家能夠大致對轉(zhuǎn)賬交易有個印象。
動態(tài)圖演示了新區(qū)塊中的輸入交易引用的是哪個區(qū)塊中的交易輸出,從而實現(xiàn)了區(qū)塊鏈每次轉(zhuǎn)賬的金額都有據(jù)可依,也從另外一個角度展示了比特幣中UTXO的概念模型。
由于代碼量巨大,為了讓整個過程的理解更加流程,我改變前面幾篇文章的思路,從執(zhí)行命令的代碼塊進(jìn)行一步一步的代碼分析,希望能將自己的思路理順,從而可以更好得引導(dǎo)讀者朋友。
同樣,因為很多概念性的東西,我不準(zhǔn)備在文章里面啰嗦,如果感覺閱讀難度比較大,建議先仔細(xì)閱讀這篇文章
https://github.com/liuchengxu/blockchain-tutorial
然后再回頭來看我的這篇文章,會事半功倍
func (cli *CLI) Send(from, to, amount []string) {
bc := GetBlockChainObject()
if bc == nil {
fmt.Println("沒有BlockChain,無法轉(zhuǎn)賬。。")
os.Exit(1)
}
defer bc.DB.Close()
bc.MineNewBlock(from, to, amount)
utsoSet :=&UTXOSet{bc}
utsoSet.Update()
}
func GetBlockChainObject() *BlockChain {
/*
1.數(shù)據(jù)庫存在,讀取數(shù)據(jù)庫,返回blockchain即可
2.數(shù)據(jù)庫 不存在,返回nil
*/
if dbExists() {
//fmt.Println("數(shù)據(jù)庫已經(jīng)存在。。。")
//打開數(shù)據(jù)庫
db, err := bolt.Open(DBName, 0600, nil)
if err != nil {
log.Panic(err)
}
var blockchain *BlockChain
err = db.View(func(tx *bolt.Tx) error {
//打開bucket,讀取l對應(yīng)的最新的hash
b := tx.Bucket([]byte(BlockBucketName))
if b != nil {
//讀取最新hash
hash := b.Get([]byte("l"))
blockchain = &BlockChain{db, hash}
}
return nil
})
if err != nil {
log.Panic(err)
}
return blockchain
} else {
fmt.Println("數(shù)據(jù)庫不存在,無法獲取BlockChain對象。。。")
return nil
}
}
判斷存儲區(qū)塊的DB文件是否存在,如果存在,直接從數(shù)據(jù)庫Bucket中讀取"l"對應(yīng)Hash值,將db對象與獲取到hash值賦值給需要返回的區(qū)塊鏈對象,如果DB文件不存在,說明創(chuàng)世區(qū)塊并未創(chuàng)建,沒有區(qū)塊鏈對象,直接退出程序。
獲取到區(qū)塊鏈對象之后,我們調(diào)用MineNewBlock方法進(jìn)行區(qū)塊的創(chuàng)建
func (bc *BlockChain) MineNewBlock(from, to, amount []string) {
/*
1.新建交易
2.新建區(qū)塊:
讀取數(shù)據(jù)庫,獲取最后一塊block
3.存入到數(shù)據(jù)庫中
*/
//1.新建交易集合
var txs [] *Transaction
utxoSet := &UTXOSet{bc}
for i := 0; i < len(from); i++ {
//amount[0]-->int
amountInt, _ := strconv.ParseInt(amount[i], 10, 64)
tx := NewSimpleTransaction(from[i], to[i], amountInt, utxoSet, txs)
txs = append(txs, tx)
}
/*
分析:循環(huán)第一次:i=0
txs[transaction1, ]
循環(huán)第二次:i=1
txs [transaction1, transaction2]
*/
//交易的驗證:
for _, tx := range txs {
if bc.VerifityTransaction(tx, txs) == false {
log.Panic("數(shù)字簽名驗證失敗。。。")
}
}
/*
獎勵:reward:
創(chuàng)建一個CoinBase交易--->Tx
*/
coinBaseTransaction := NewCoinBaseTransaction(from[0])
txs = append(txs, coinBaseTransaction)
//2.新建區(qū)塊
newBlock := new(Block)
err := bc.DB.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(BlockBucketName))
if b != nil {
//讀取數(shù)據(jù)庫
blockBytes := b.Get(bc.Tip)
lastBlock := DeserializeBlock(blockBytes)
newBlock = NewBlock(txs, lastBlock.Hash, lastBlock.Height+1)
}
return nil
})
if err != nil {
log.Panic(err)
}
//3.存入到數(shù)據(jù)庫中
err = bc.DB.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(BlockBucketName))
if b != nil {
//將新block存入到數(shù)據(jù)庫中
b.Put(newBlock.Hash, newBlock.Serialize())
//更新l
b.Put([]byte("l"), newBlock.Hash)
//tip
bc.Tip = newBlock.Hash
}
return nil
})
if err != nil {
log.Panic(err)
}
}
在以上的代碼中,涉及到幾個比較重要的方法,其中一個NewSimpleTransaction用于創(chuàng)建交易并打包,這里對代碼進(jìn)行了簡單梳理,由于內(nèi)容實在太多,在文章末尾我會將github的源代碼地址貼出,供大家查看。
NewSimpleTransaction
func NewSimpleTransaction(from, to string, amount int64, utxoSet *UTXOSet, txs []*Transaction) *Transaction {
//1.定義Input和Output的數(shù)組
var txInputs []*TxInput
var txOuputs [] *TxOutput
//2.創(chuàng)建Input
/*
創(chuàng)世區(qū)塊中交易ID:c16d3ad93450cd532dcd7ef53d8f396e46b2e59aa853ad44c284314c7b9db1b4
*/
//獲取本次轉(zhuǎn)賬要使用output
//total, spentableUTXO := bc.FindSpentableUTXOs(from, amount, txs) //map[txID]-->[]int{index}
total, spentableUTXO := utxoSet.FindSpentableUTXOs(from, amount, txs) //map[txID]-->[]int{index}
//獲取錢包的集合:
wallets := GetWallets()
wallet := wallets.WalletMap[from]
for txID, indexArray := range spentableUTXO {
txIDBytes, _ := hex.DecodeString(txID)
for _, index := range indexArray {
txInput := &TxInput{txIDBytes, index, nil, wallet.PublickKey}
txInputs = append(txInputs, txInput)
}
}
//3.創(chuàng)建Output
//轉(zhuǎn)賬
txOutput := NewTxOutput(amount, to)
txOuputs = append(txOuputs, txOutput)
//找零
//txOutput2 := &TxOutput{total - amount, from}
txOutput2 := NewTxOutput(total-amount, from)
txOuputs = append(txOuputs, txOutput2)
//4.創(chuàng)建交易
tx := &Transaction{[]byte{}, txInputs, txOuputs}
//設(shè)置交易的ID
tx.SetID()
//設(shè)置簽名
utxoSet.BlockChian.SignTransaction(tx,wallet.PrivateKey,txs)
return tx
}
FindSpentableUTXOs
func (utxoSet *UTXOSet) FindSpentableUTXOs(from string, amount int64, txs []*Transaction) (int64, map[string][]int) {
var total int64
//用于存儲轉(zhuǎn)賬所使用utxo
spentableUTXOMap := make(map[string][]int)
//1.查詢未打包可以使用的utxo:txs
unPackageSpentableUTXOs := utxoSet.FindUnpackeSpentableUTXO(from, txs)
for _, utxo := range unPackageSpentableUTXOs {
total += utxo.Output.Value
txIDStr := hex.EncodeToString(utxo.TxID)
spentableUTXOMap[txIDStr] = append(spentableUTXOMap[txIDStr], utxo.Index)
if total >= amount {
return total, spentableUTXOMap
}
}
//2.查詢utxotable,查詢utxo
//已經(jīng)存儲的但是未花費的utxo
err := utxoSet.BlockChian.DB.View(func(tx *bolt.Tx) error {
//查詢utxotable中,未花費的utxo
b := tx.Bucket([]byte(utxosettable))
if b != nil {
//查詢
c := b.Cursor()
dbLoop:
for k, v := c.First(); k != nil; k, v = c.Next() {
txOutputs := DeserializeTxOutputs(v)
for _, utxo := range txOutputs.UTXOs {
if utxo.Output.UnlockWithAddress(from) {
total += utxo.Output.Value
txIDStr := hex.EncodeToString(utxo.TxID)
spentableUTXOMap[txIDStr] = append(spentableUTXOMap[txIDStr], utxo.Index)
if total >= amount {
break dbLoop
//return nil
}
}
}
}
}
return nil
})
if err != nil {
log.Panic(err)
}
return total, spentableUTXOMap
}
FindUnpackeSpentableUTXO
func (utxoSet *UTXOSet) FindUnpackeSpentableUTXO(from string, txs []*Transaction) []*UTXO {
//存儲可以使用的未花費utxo
var unUTXOs []*UTXO
//存儲已經(jīng)花費的input
spentedMap := make(map[string][]int)
for i := len(txs) - 1; i >= 0; i-- {
//func caculate(tx *Transaction, address string, spentTxOutputMap map[string][]int, unSpentUTXOs []*UTXO) []*UTXO {
unUTXOs = caculate(txs[i], from, spentedMap, unUTXOs)
}
return unUTXOs
}
caculate
func caculate(tx *Transaction, address string, spentTxOutputMap map[string][]int, unSpentUTXOs []*UTXO) []*UTXO {
//遍歷每個tx:txID,Vins,Vouts
//遍歷所有的TxInput
if !tx.IsCoinBaseTransaction() { //tx不是CoinBase交易,遍歷TxInput
for _, txInput := range tx.Vins {
//txInput-->TxInput
full_payload := Base58Decode([]byte(address))
pubKeyHash := full_payload[1 : len(full_payload)-addressCheckSumLen]
if txInput.UnlockWithAddress(pubKeyHash) {
//txInput的解鎖腳本(用戶名) 如果和要查詢的余額的用戶名相同,
key := hex.EncodeToString(txInput.TxID)
spentTxOutputMap[key] = append(spentTxOutputMap[key], txInput.Vout)
/*
map[key]-->value
map[key] -->[]int
*/
}
}
}
//遍歷所有的TxOutput
outputs:
for index, txOutput := range tx.Vouts { //index= 0,txoutput.鎖定腳本
if txOutput.UnlockWithAddress(address) {
if len(spentTxOutputMap) != 0 {
var isSpentOutput bool //false
//遍歷map
for txID, indexArray := range spentTxOutputMap { //143d,[]int{1}
//遍歷 記錄已經(jīng)花費的下標(biāo)的數(shù)組
for _, i := range indexArray {
if i == index && hex.EncodeToString(tx.TxID) == txID {
isSpentOutput = true //標(biāo)記當(dāng)前的txOutput是已經(jīng)花費
continue outputs
}
}
}
if !isSpentOutput {
//unSpentTxOutput = append(unSpentTxOutput, txOutput)
//根據(jù)未花費的output,創(chuàng)建utxo對象--->數(shù)組
utxo := &UTXO{tx.TxID, index, txOutput}
unSpentUTXOs = append(unSpentUTXOs, utxo)
}
} else {
//如果map長度為0,證明還沒有花費記錄,output無需判斷
//unSpentTxOutput = append(unSpentTxOutput, txOutput)
utxo := &UTXO{tx.TxID, index, txOutput}
unSpentUTXOs = append(unSpentUTXOs, utxo)
}
}
}
return unSpentUTXOs
}
SignTransaction
func (bc *BlockChain) SignTransaction(tx *Transaction, privateKey ecdsa.PrivateKey, txs []*Transaction) {
//1.判斷要簽名的tx,如果是coninbase交易直接返回
if tx.IsCoinBaseTransaction() {
return
}
//2.獲取該tx中的Input,引用之前的transaction中的未花費的output
prevTxs := make(map[string]*Transaction)
for _, input := range tx.Vins {
txIDStr := hex.EncodeToString(input.TxID)
prevTxs[txIDStr] = bc.FindTransactionByTxID(input.TxID, txs)
}
//3.簽名
tx.Sign(privateKey, prevTxs)
}
Sign
func (tx *Transaction) Sign(privateKey ecdsa.PrivateKey, prevTxsmap map[string]*Transaction) {
//1.判斷當(dāng)前tx是否是coinbase交易
if tx.IsCoinBaseTransaction() {
return
}
//2.獲取input對應(yīng)的output所在的tx,如果不存在,無法進(jìn)行簽名
for _, input := range tx.Vins {
if prevTxsmap[hex.EncodeToString(input.TxID)] == nil {
log.Panic("當(dāng)前的Input,沒有找到對應(yīng)的output所在的Transaction,無法簽名。。")
}
}
//即將進(jìn)行簽名:私鑰,要簽名的數(shù)據(jù)
txCopy := tx.TrimmedCopy()
for index, input := range txCopy.Vins {
prevTx := prevTxsmap[hex.EncodeToString(input.TxID)]
txCopy.Vins[index].Signature = nil
txCopy.Vins[index].PublicKey = prevTx.Vouts[input.Vout].PubKeyHash //設(shè)置input中的publickey為對應(yīng)的output的公鑰哈希
txCopy.TxID = txCopy.NewTxID()//產(chǎn)生要簽名的交易的TxID
//為了方便下一個input,將數(shù)據(jù)再置為空
txCopy.Vins[index].PublicKey = nil
/*
第一個參數(shù)
第二個參數(shù):私鑰
第三個參數(shù):要簽名的數(shù)據(jù)
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error)
r + s--->sign
input.Signatrue = sign
*/
r,s,err:=ecdsa.Sign(rand.Reader, &privateKey, txCopy.TxID )
if err != nil{
log.Panic(err)
}
sign:=append(r.Bytes(),s.Bytes()...)
tx.Vins[index].Signature = sign
}
}
TrimmedCopy
func (tx *Transaction) TrimmedCopy() *Transaction {
var inputs [] *TxInput
var outputs [] *TxOutput
for _, in := range tx.Vins {
inputs = append(inputs, &TxInput{in.TxID, in.Vout, nil, nil})
}
for _, out := range tx.Vouts {
outputs = append(outputs, &TxOutput{out.Value, out.PubKeyHash})
}
txCopy := &Transaction{tx.TxID, inputs, outputs}
return txCopy
}
func (tx *Transaction) Verifity(prevTxs map[string]*Transaction)bool{
//1.如果是coinbase交易,不需要驗證
if tx.IsCoinBaseTransaction(){
return true
}
//prevTxs
for _,input:=range prevTxs{
if prevTxs[hex.EncodeToString(input.TxID)] == nil{
log.Panic("當(dāng)前的input沒有找到對應(yīng)的Transaction,無法驗證。。")
}
}
//驗證
txCopy:= tx.TrimmedCopy()
curev:= elliptic.P256() //曲線
for index,input:=range tx.Vins{
//原理:再次獲取 要簽名的數(shù)據(jù) + 公鑰哈希 + 簽名
/*
驗證簽名的有效性:
第一個參數(shù):公鑰
第二個參數(shù):簽名的數(shù)據(jù)
第三、四個參數(shù):簽名:r,s
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool
*/
//ecdsa.Verify()
//獲取要簽名的數(shù)據(jù)
prevTx:=prevTxs[hex.EncodeToString(input.TxID)]
txCopy.Vins[index].Signature = nil
txCopy.Vins[index].PublicKey = prevTx.Vouts[input.Vout].PubKeyHash
txCopy.TxID = txCopy.NewTxID() //要簽名的數(shù)據(jù)
txCopy.Vins[index].PublicKey = nil
//獲取公鑰
/*
type PublicKey struct {
elliptic.Curve
X, Y *big.Int
}
*/
x:=big.Int{}
y:=big.Int{}
keyLen:=len(input.PublicKey)
x.SetBytes(input.PublicKey[:keyLen/2])
y.SetBytes(input.PublicKey[keyLen/2:])
rawPublicKey:=ecdsa.PublicKey{curev,&x,&y}
//獲取簽名:
r :=big.Int{}
s :=big.Int{}
signLen:=len(input.Signature)
r.SetBytes(input.Signature[:signLen/2])
s.SetBytes(input.Signature[signLen/2:])
if ecdsa.Verify(&rawPublicKey,txCopy.TxID,&r,&s) == false{
return false
}
}
return true
}
func NewCoinBaseTransaction(address string) *Transaction {
txInput := &TxInput{[]byte{}, -1, nil, nil}
//txOutput := &TxOutput{10, address}
txOutput := NewTxOutput(10, address)
txCoinBaseTransaction := &Transaction{[]byte{}, []*TxInput{txInput}, []*TxOutput{txOutput}}
//設(shè)置交易ID
txCoinBaseTransaction.SetID()
return txCoinBaseTransaction
}
在每個區(qū)塊中創(chuàng)建一個CoinBase交易作為獎勵機(jī)制。
func NewBlock(txs []*Transaction, prevBlockHash [] byte, height int64) *Block {
//創(chuàng)建區(qū)塊
block := &Block{height, prevBlockHash, txs, time.Now().Unix(), nil,0}
//設(shè)置hash
//block.SetHash()
pow:=NewProofOfWork(block)
hash,nonce:=pow.Run()
block.Hash = hash
block.Nonce = nonce
return block
}
err = bc.DB.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(BlockBucketName))
if b != nil {
//將新block存入到數(shù)據(jù)庫中
b.Put(newBlock.Hash, newBlock.Serialize())
//更新l
b.Put([]byte("l"), newBlock.Hash)
//tip
bc.Tip = newBlock.Hash
}
return nil
})
if err != nil {
log.Panic(err)
}
func (utxoSet *UTXOSet) Update() {
/*
表:key:txID
value:TxOutputs
UTXOs []UTXO
*/
//1.獲取最后(從后超前遍歷)一個區(qū)塊,遍歷該區(qū)塊中的所有tx
newBlock := utxoSet.BlockChian.Iterator().Next()
//2.獲取所有的input
inputs := [] *TxInput{}
//遍歷交易,獲取所有的input
for _, tx := range newBlock.Txs {
if !tx.IsCoinBaseTransaction() {
for _, in := range tx.Vins {
inputs = append(inputs, in)
}
}
}
//存儲該區(qū)塊中的,tx中的未花費
outsMap := make(map[string]*TxOutputs)
//3.獲取所有的output
for _, tx := range newBlock.Txs {
utxos := []*UTXO{}
//找出交易中的未花費
for index, output := range tx.Vouts {
isSpent := false
//遍歷inputs的數(shù)組,比較是否有intput和該output對應(yīng),如果滿足,表示花費了
for _, input := range inputs {
if bytes.Compare(tx.TxID, input.TxID) == 0 && index == input.Vout {
if bytes.Compare(output.PubKeyHash, PubKeyHash(input.PublicKey)) == 0 {
isSpent = true
}
}
}
if isSpent == false {
//output未花
utxo := &UTXO{tx.TxID, index, output}
utxos = append(utxos, utxo)
}
}
//utxos,
if len(utxos) > 0 {
txIDStr := hex.EncodeToString(tx.TxID)
outsMap[txIDStr] = &TxOutputs{utxos}
}
}
//刪除花費了數(shù)據(jù),添加未花費
err := utxoSet.BlockChian.DB.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(utxosettable))
if b != nil {
//遍歷inputs,刪除
for _, input := range inputs {
txOutputsBytes := b.Get(input.TxID)
if len(txOutputsBytes) == 0 {
continue
}
//反序列化
txOutputs := DeserializeTxOutputs(txOutputsBytes)
//是否需要被刪除
isNeedDelete := false
//存儲該txoutout中未花費utxo
utxos := []*UTXO{}
for _, utxo := range txOutputs.UTXOs {
if bytes.Compare(utxo.Output.PubKeyHash, PubKeyHash(input.PublicKey)) == 0 && input.Vout == utxo.Index {
isNeedDelete = true
} else {
utxos = append(utxos, utxo)
}
}
if isNeedDelete == true {
b.Delete(input.TxID)
if len(utxos) > 0 {
txOutputs := &TxOutputs{utxos}
b.Put(input.TxID, txOutputs.Serialize())
}
}
}
//遍歷map,添加
for txIDStr, txOutputs := range outsMap {
txID, _ := hex.DecodeString(txIDStr)
b.Put(txID, txOutputs.Serialize())
}
}
return nil
})
if err != nil {
log.Panic(err)
}
}
由于轉(zhuǎn)賬交易這一塊的內(nèi)容代碼量特別大,腦圖跟交易流程圖我也是花費了大量的時間進(jìn)行整理,但是要一項一項進(jìn)行代碼分析,時間成本還是太大了,所以,將github的代碼共享給大家,可以照著文章思路與思維導(dǎo)圖中的路線進(jìn)行適當(dāng)?shù)姆治?https://github.com/DiaboFong/MyPublicChain
網(wǎng)頁標(biāo)題:從0到1簡易區(qū)塊鏈開發(fā)手冊V0.4-實現(xiàn)轉(zhuǎn)賬交易的思路分析
分享路徑:http://aaarwkj.com/article4/gppcoe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)網(wǎng)站建設(shè)、Google、品牌網(wǎng)站設(shè)計、關(guān)鍵詞優(yōu)化、定制開發(fā)、企業(yè)網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)