Go實(shí)現(xiàn)區(qū)塊鏈(四)---交易事物(一)

1.前言

上一篇我知道了區(qū)塊鏈如何持久化存儲(chǔ),接下來我們將開始實(shí)現(xiàn)區(qū)塊鏈中交易是如何產(chǎn)生的如何防止被串改物舒,如何在網(wǎng)絡(luò)中分布式記賬。我們將交易分成兩部分:交易實(shí)現(xiàn)一般機(jī)制,后面將實(shí)現(xiàn)網(wǎng)絡(luò)坊萝、獎(jiǎng)勵(lì)機(jī)制等隶症。

2.知識(shí)準(zhǔn)備

知識(shí)點(diǎn) 學(xué)習(xí)網(wǎng)頁 特性
bitcoin交易 交易 不可串改
Coinbase 創(chuàng)世塊交易信息 創(chuàng)世塊交易

3.基本交易過程

在區(qū)塊鏈中每次發(fā)生交易,用戶需要將新的交易記錄寫到比特幣區(qū)塊鏈網(wǎng)絡(luò)中龙屉,等待網(wǎng)絡(luò)確認(rèn)為交易完成呐粘。每個(gè)交易包括了一些輸入和一些輸出,未經(jīng)使用的交易的輸出(Transaction Outputs,UTXO)可以被新的交易引用作為合法輸入转捕,被使用過的交易的輸出(Spent Transaction Outputs作岖,STO)則無法被引用作為合法輸入。

注意:這里的比特幣交易與我們傳統(tǒng)的金錢付款交易是不同的五芝,并沒有賬號痘儡、沒有余額等。詳情參考

4.代碼實(shí)現(xiàn)

交易:

//交易事物
type Transaction struct {
    ID   []byte     //交易hash
    Vin  []TXInput  //事物輸入
    Vout []TXOutput //事物輸出
}

流程圖:


交易引用圖

注意:

  • 有些輸出和輸入無關(guān)枢步。
  • 在一個(gè)交易中沉删,投入可以參考多個(gè)交易的輸出。
  • 輸入必須引用輸出醉途。

比特幣中沒有這樣的概念矾瑰。事務(wù)只是用腳本鎖定值,只能由鎖定它的人解鎖隘擎。

交易輸出:

//一個(gè)事物輸出
type TXOutput struct {
    Value int       //值
    ScriptPubKey string //解鎖腳本key
}

實(shí)際上殴穴,它是存儲(chǔ)“硬幣”的輸出(注意Value上面的字段)。而存儲(chǔ)意味著用一個(gè)拼圖鎖定它們嵌屎,這是存儲(chǔ)在ScriptPubKey推正。在內(nèi)部,比特幣使用稱為腳本的腳本語言宝惰,用于定義輸出鎖定和解鎖邏輯。這個(gè)語言很原始(這是故意的再沧,以避免可能的黑客和濫用)尼夺,但我們不會(huì)詳細(xì)討論它。你可以在本章知識(shí)點(diǎn)bitcoin交易詳細(xì)解釋。

在比特幣中淤堵,價(jià)值領(lǐng)域存儲(chǔ)satoshis的數(shù)量寝衫,而不是BTC的數(shù)量。甲聰是100000000分之1一個(gè)比特幣(0.00000001 BTC)的拐邪,因此慰毅,這是貨幣的比特幣的最小單位(如百分比)。

由于我們沒有實(shí)現(xiàn)地址扎阶,現(xiàn)在我們將避免整個(gè)腳本相關(guān)的邏輯汹胃。ScriptPubKey將存儲(chǔ)任意字符串(用戶定義的錢包地址)。

交易輸入:

//一個(gè)事物輸入
type TXInput struct {
    Txid []byte //交易ID的hash
    Vout int    //交易輸出
    ScriptSig string //解鎖腳本
}

如前所述东臀,輸入引用前一個(gè)輸出:Txid存儲(chǔ)此類事務(wù)的ID着饥,并Vout在事務(wù)中存儲(chǔ)輸出的索引。ScriptSig是一個(gè)提供數(shù)據(jù)以在輸出中使用的腳本ScriptPubKey惰赋。如果數(shù)據(jù)是正確的宰掉,輸出可以被解鎖,并且它的值可以被用來產(chǎn)生新的輸出; 如果不正確赁濒,則輸出中不能引用輸出轨奄。這是保證用戶不能花錢屬于其他人的硬幣的機(jī)制。

同樣拒炎,由于我們還沒有實(shí)現(xiàn)地址戚绕,ScriptSig因此將只存儲(chǔ)任意用戶定義的錢包地址。我們將在下一篇文章中實(shí)現(xiàn)公鑰和簽名檢查枝冀。

我們總結(jié)一下舞丛。產(chǎn)出是儲(chǔ)存“硬幣”的地方。每個(gè)輸出都帶有一個(gè)解鎖腳本果漾,它決定了解鎖輸出的邏輯球切。每個(gè)新事務(wù)都必須至少有一個(gè)輸入和輸出。輸入引用前一個(gè)事務(wù)的輸出绒障,并提供ScriptSig輸出的解鎖腳本中使用的數(shù)據(jù)(字段)吨凑,以解除鎖定并使用其值創(chuàng)建新的輸出。

coinbase交易:
上面我們知道輸入?yún)⒖驾敵鲞壿嫽瑁敵鲇謪⒖剂溯斎胪叶邸_@樣就產(chǎn)生了我們常見的一個(gè)問題:先有雞還是先有蛋呢?

當(dāng)?shù)V工開始挖礦時(shí),它會(huì)添加一個(gè)coinbase交易庐镐。coinbase交易是一種特殊類型的交易恩商,不需要以前存在的輸出。它無處不在地創(chuàng)造產(chǎn)出(即“硬幣”)必逆。沒有雞的雞蛋怠堪。這是礦工獲得開采新礦區(qū)的獎(jiǎng)勵(lì)揽乱。

如您所知,區(qū)塊鏈開始處有起始塊粟矿。這個(gè)區(qū)塊在區(qū)塊鏈中產(chǎn)生了第一個(gè)輸出凰棉。由于沒有以前的交易并且沒有這樣的輸出,因此不需要先前的輸出陌粹。

我們來創(chuàng)建一個(gè)coinbase事務(wù):

//創(chuàng)建Coinbase事物
func NewCoinbaseTX(to,data string) *Transaction  {
    if data==""{
        data=fmt.Sprintf("Reward to '%s'",to)
    }
    //這里Vout-1 data:const genesisCoinbaseData = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"
    txin :=TXInput{[]byte{},-1,data}
    //subsidy是獎(jiǎng)勵(lì)的金額  
    txout := TXOutput{subsidy, to}
    tx := Transaction{nil, []TXInput{txin}, []TXOutput{txout}}
    //設(shè)置32位交易hash
    tx.SetID()
    return &tx
}

//設(shè)置交易ID hash
func (tx *Transaction) SetID(){
    var encoded bytes.Buffer
    var hash [32]byte //32位的hash字節(jié)

    enc := gob.NewEncoder(&encoded)
    err := enc.Encode(tx)
    if err != nil {
        log.Panic(err)
    }
    //將交易信息sha256
    hash = sha256.Sum256(encoded.Bytes())
    //生成hash
    tx.ID = hash[:]
}

一個(gè)coinbase交易只有一個(gè)輸入撒犀。在我們的實(shí)現(xiàn)中它Txid是空的,Vout等于-1掏秩。另外或舞,coinbase事務(wù)不會(huì)存儲(chǔ)腳本ScriptSig。相反哗讥,任意數(shù)據(jù)存儲(chǔ)在那里嚷那。

在比特幣中,第一個(gè)coinbase交易包含以下信息:“The Times 03/Jan/2009 Chancellor on brink of second bailout for banks”杆煞。查看知識(shí)點(diǎn)Coinbase可以知道魏宽。

subsidy是獎(jiǎng)勵(lì)的金額。在比特幣中决乎,這個(gè)數(shù)字沒有存儲(chǔ)在任何地方队询,只根據(jù)塊的總數(shù)進(jìn)行計(jì)算:塊的數(shù)量除以210000。挖掘創(chuàng)世紀(jì)塊產(chǎn)生50 BTC构诚,每210000塊獎(jiǎng)勵(lì)減半蚌斩。在我們的實(shí)現(xiàn)中,我們會(huì)將獎(jiǎng)勵(lì)作為常量存儲(chǔ)(至少現(xiàn)在是??)范嘱。

在區(qū)塊鏈中存儲(chǔ)交易:
我們將開始區(qū)塊里面的data改成transactions

//區(qū)塊結(jié)構(gòu)
type Block struct {
    Hash          []byte //hase值
    Transactions []*Transaction//交易數(shù)據(jù)
    PrevBlockHash []byte //存儲(chǔ)前一個(gè)區(qū)塊的Hase值
    Timestamp     int64  //生成區(qū)塊的時(shí)間
    Nonce         int    //工作量證明算法的計(jì)數(shù)器
}

對應(yīng)的NewBlock送膳,NewGensisBlock也應(yīng)該修改:

//生成一個(gè)新的區(qū)塊方法
func NewBlock(transactions []*Transaction, prevBlockHash []byte) *Block{
    //GO語言給Block賦值{}里面屬性順序可以打亂,但必須制定元素 如{Timestamp:time.Now().Unix()...}
    block := &Block{Timestamp:time.Now().Unix(), Transactions:transactions, PrevBlockHash:prevBlockHash, Hash:[]byte{},Nonce:0}

    //工作證明
    pow :=NewProofOfWork(block)
    //工作量證明返回計(jì)數(shù)器和hash
    nonce, hash := pow.Run()
    block.Hash = hash[:]
    block.Nonce = nonce
    return block
}

//創(chuàng)建并返回創(chuàng)世紀(jì)Block
func  NewGenesisBlock(coinbase *Transaction) *Block {
    return NewBlock([]*Transaction{coinbase}, []byte{})
}

blockchain:

/ 創(chuàng)新一個(gè)新的區(qū)塊數(shù)據(jù)
func CreateBlockchain(address string) *Blockchain {
    ...
    err = db.Update(func(tx *bolt.Tx) error {
        cbtx := NewCoinbaseTX(address, genesisCoinbaseData)
        genesis := NewGenesisBlock(cbtx)
        b, err := tx.CreateBucket([]byte(blocksBucket))
        if err != nil {
            log.Panic(err)
        }
        err = b.Put(genesis.Hash, genesis.Serialize())
        ...
}

這里,函數(shù)將獲得一個(gè)地址丑蛤,該地址將獲得挖掘創(chuàng)世塊的獎(jiǎng)勵(lì)叠聋。(我們這里獎(jiǎng)勵(lì)為10)

工作量證明:

Proof-of-Work算法必須考慮存儲(chǔ)在塊中的事務(wù),以保證區(qū)塊鏈作為事務(wù)存儲(chǔ)的一致性和可靠性受裹。所以現(xiàn)在我們必須修改ProofOfWork.prepareData方法:

//將區(qū)塊體里面的數(shù)據(jù)轉(zhuǎn)換成一個(gè)字節(jié)碼數(shù)組碌补,為下一個(gè)區(qū)塊準(zhǔn)備數(shù)據(jù)
func (pow *ProofOfWork) prepareData(nonce int) []byte {
    //注意一定要將原始數(shù)據(jù)轉(zhuǎn)換成[]byte,不能直接從字符串轉(zhuǎn)
    data := bytes.Join(
        [][]byte{
            pow.block.PrevBlockHash,
            pow.block.HashTransactions(),
            utils.IntToHex(pow.block.Timestamp),
            utils.IntToHex(int64(targetBits)),
            utils.IntToHex(int64(nonce)),
        },
        []byte{},
    )
    return data
}

將data改成hashTransactions:

//返回塊狀事務(wù)的hash
func (b *Block) HashTransactions() []byte {
  var txHashes [][]byte
  var txHash [32]byte
  for _, tx := range b.Transactions {
        txHashes = append(txHashes, tx.ID)
    }
    txHash = sha256.Sum256(bytes.Join(txHashes, []byte{}))

    return txHash[:]
}

我們使用散列作為提供數(shù)據(jù)的唯一表示的機(jī)制棉饶。我們希望塊中的所有事務(wù)都由一個(gè)散列唯一標(biāo)識(shí)厦章。為了達(dá)到這個(gè)目的,我們得到每個(gè)事務(wù)的哈希值照藻,連接它們袜啃,并獲得連接組合的哈希值。

比特幣使用更復(fù)雜的技術(shù):它將所有包含在塊中的事務(wù)表示為Merkle樹岩梳,并在Proof-of-Work系統(tǒng)中使用樹的根散列囊骤。這種方法允許快速檢查塊是否包含某個(gè)事務(wù)晃择,只有根散列并且不下載所有事務(wù)冀值。(后續(xù)會(huì)詳細(xì)講解Merkle算法)

好了我們現(xiàn)在嘗試一下CreateBlockchain:
編譯:

C:\go-worke\src\github.com\study-bitcoin-go>go build github.com/study-bitcoin-go

執(zhí)行createblockchain命令:

C:\go-worke\src\github.com\study-bitcoin-go>study-bitcoin-go createblockchain -address even

輸出:

Dig into mine  00000860adb64e2ca9c83d0f665f7ec3148ec9d32b64cb97f2481712c4d94d79

Done!

目前為止我們實(shí)現(xiàn)開采創(chuàng)世塊獎(jiǎng)勵(lì)也物。但我們要如何實(shí)現(xiàn)查詢余額呢?

未使用交易輸出
我們需要找到所有未使用的交易輸出(UTXO)列疗。未使用意味著這些輸出在任何輸入中都未被引用滑蚯。在上圖中,這些是:

  1. tx0抵栈,輸出1;
  2. tx1告材,輸出0;
  3. tx3,輸出0;
  4. tx4古劲,輸出0斥赋。

當(dāng)然,當(dāng)我們檢查余額時(shí)产艾,我們不需要所有這些疤剑,但只有那些可以用我們擁有的密鑰解鎖的(當(dāng)前我們沒有實(shí)現(xiàn)密鑰并且將使用用戶定義的地址)。首先闷堡,我們來定義輸入和輸出上的鎖定 - 解鎖方法:

//通過檢查地址是否啟動(dòng)了事務(wù)
func (in *TXInput) CanUnlockOutputWith(unlockingData string) bool {
    return in.ScriptSig == unlockingData
}
//檢查輸出是否可以使用所提供的數(shù)據(jù)進(jìn)行解鎖
func (out *TXOutput) CanBeUnlockedWith(unlockingData string) bool {
    return out.ScriptPubKey == unlockingData
}

這里我們只是比較腳本字段unlockingData隘膘。在我們實(shí)現(xiàn)基于私鑰的地址后,這些作品將在未來的文章中得到改進(jìn)杠览。
下一步 - 查找包含未使用產(chǎn)出的交易 - 相當(dāng)困難:

//查詢未處理的事務(wù)
func (bc *Blockchain) FindUnspentTransactions(address string) []Transaction {
    var unspentTXs []Transaction //未處理的事務(wù)
    spentTXOs := make(map[string][]int)
    bci := bc.Iterator()

    for {
        block := bci.Next()
        for _,tx := range block.Transactions {
            txID := hex.EncodeToString(tx.ID)  //交易ID轉(zhuǎn)換成string
        Outputs:
            for outIdx, out := range tx.Vout {
                // Was the output spent?
                if spentTXOs[txID] != nil {
                    //檢查一個(gè)輸出是否已經(jīng)在輸入中被引用
                    for _, spentOut := range spentTXOs[txID] {
                        if spentOut == outIdx {
                            continue Outputs
                        }
                    }
                }
                //由于交易存儲(chǔ)在塊中弯菊,因此我們必須檢查區(qū)塊鏈中的每個(gè)塊。我們從輸出開始:
                if out.CanBeUnlockedWith(address) {
                    unspentTXs = append(unspentTXs, *tx)
                }
            }

            //我們跳過輸入中引用的那些(它們的值被移到其他輸出踱阿,因此我們不能計(jì)數(shù)它們)管钳。
            // 在檢查輸出之后,我們收集所有可能解鎖輸出的輸入软舌,并鎖定提供的地址(這不適用于coinbase事務(wù)才漆,因?yàn)樗鼈儾唤怄i輸出)
            if tx.IsCoinbase() == false {
                for _, in := range tx.Vin {
                    if in.CanUnlockOutputWith(address) {
                        inTxID := hex.EncodeToString(in.Txid)
                        spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout)
                    }
                }
            }
        }
        if len(block.PrevBlockHash) == 0 {
            break
        }
    }
    return unspentTXs
}

該函數(shù)返回一個(gè)包含未使用輸出的事務(wù)列表。為了計(jì)算余額葫隙,我們需要一個(gè)函數(shù)來處理事務(wù)并僅返回輸出:

//發(fā)現(xiàn)并返回所有未使用的事務(wù)輸出
func (bc *Blockchain) FindUTXO(address string) []TXOutput {
    var UTXOs []TXOutput
    //未使用輸出的事務(wù)列表
    unspentTransactions := bc.FindUnspentTransactions(address)
    //查找
    for _, tx := range unspentTransactions {
        for _, out := range tx.Vout {
            ///檢查輸出是否可以使用所提供的數(shù)據(jù)進(jìn)行解鎖
            if out.CanBeUnlockedWith(address) {
                UTXOs = append(UTXOs, out)
            }
        }
    }
    return UTXOs
}

客戶端cli getbalance命令:

//查詢余額
func (cli *CLI) getBalance(address string) {
    bc := block.NewBlockchain(address)
    defer block.Close(bc)

    balance := 0
    //查詢所有未經(jīng)使用的交易地址
    UTXOs := bc.FindUTXO(address)
    //算出未使用的交易地址的value和
    for _, out := range UTXOs {
        balance += out.Value
    }
    fmt.Printf("Balance of '%s': %d\n", address, balance)
}

賬戶余額是由賬戶地址鎖定的所有未使用的交易輸出值的總和栽烂。

現(xiàn)在我們檢查一下地址為even的錢:
重新編譯后

C:\go-worke\src\github.com\study-bitcoin-go>study-bitcoin-go getbalance -address even
Balance of 'even': 10

這就是第一筆錢,接下來我們需要實(shí)現(xiàn)給一個(gè)地址轉(zhuǎn)幣

現(xiàn)在,我們要發(fā)送一些硬幣給別人恋脚。為此腺办,我們需要?jiǎng)?chuàng)建一個(gè)新的事務(wù),將它放在一個(gè)塊中糟描,然后挖掘塊怀喉。到目前為止,我們只實(shí)現(xiàn)了coinbase交易(這是一種特殊類型的交易)船响,現(xiàn)在我們需要一個(gè)一般交易:

//創(chuàng)建一個(gè)新的未經(jīng)使用的交易輸出
func NewUTXOTransaction(from, to string, amount int, bc *Blockchain)   *Transaction{
    var inputs []TXInput
    var outputs []TXOutput
    //查詢發(fā)幣地址所未經(jīng)使用的交易輸出
    acc, validOutputs := bc.FindSpendableOutputs(from, amount)
    //判斷是否有那么多可花費(fèi)的幣
    if acc < amount {
        log.Panic("ERROR: Not enough funds")
    }
    // Build a list of inputs
    for txid, outs := range validOutputs {
        txID, err := hex.DecodeString(txid)
        if err != nil {
            log.Panic(err)
        }
        for _, out := range outs {
            input := TXInput{txID, out, from}
            inputs = append(inputs, input)
        }
    }
    // Build a list of outputs
    outputs = append(outputs, TXOutput{amount, to})
    if acc > amount {
        outputs = append(outputs, TXOutput{acc - amount, from}) // a change
    }
    tx := Transaction{nil, inputs, outputs}
    tx.SetID()
    return &tx
}

在創(chuàng)建新的輸出之前躬拢,我們首先必須找到所有未使用的輸出并確保它們存儲(chǔ)足夠的值躲履。這是什么FindSpendableOutputs方法。之后聊闯,為每個(gè)找到的輸出創(chuàng)建一個(gè)引用它的輸入工猜。接下來,我們創(chuàng)建兩個(gè)輸出:

  1. 一個(gè)與接收器地址鎖定的菱蔬。這是硬幣實(shí)際轉(zhuǎn)移到其他地址篷帅。
  2. 一個(gè)與發(fā)件人地址鎖定在一起。這是一個(gè)變化拴泌。只有在未使用的輸出持有比新事務(wù)所需的更多價(jià)值時(shí)才會(huì)創(chuàng)建魏身。記住:輸出是不可分割的蚪腐。

FindSpendableOutputs方法基于FindUnspentTransactions我們之前定義的方法:

func (bc *Blockchain) FindSpendableOutputs(address string, amount int) (int, map[string][]int)  {
    unspentOutputs := make(map[string][]int)
    unspentTXs := bc.FindUnspentTransactions(address)
    accumulated := 0

Work:
    for _, tx := range unspentTXs {
        txID := hex.EncodeToString(tx.ID)
        for outIdx, out := range tx.Vout {
            if out.CanBeUnlockedWith(address) && accumulated < amount {
                accumulated += out.Value
                unspentOutputs[txID] = append(unspentOutputs[txID], outIdx)

                if accumulated >= amount {
                    break Work
                }
            }
        }
    }
    return accumulated, unspentOutputs
}

該方法迭代所有未使用的事務(wù)并累積其值箭昵。當(dāng)累計(jì)值大于或等于我們要轉(zhuǎn)移的金額時(shí),它停止并返回按事務(wù)ID分組的累計(jì)值和輸出索引回季。我們不想花更多的錢家制。

現(xiàn)在我們可以修改該Blockchain.MineBlock方法:

//開采區(qū)塊
func (bc *Blockchain) MineBlock(transactions []*Transaction)  {
    var lastHash  []byte//最新一個(gè)hash
    err := bc.db.View(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte(blocksBucket))
        lastHash = b.Get([]byte("l"))

        return nil
    })
    if err != nil {
        log.Panic(err)
    }
    //創(chuàng)造一個(gè)新區(qū)塊
    newBlock := NewBlock(transactions, lastHash)
    //修改"l"的hash
    err = bc.db.Update(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte(blocksBucket))
        err := b.Put(newBlock.Hash, newBlock.Serialize())
        if err != nil {
            log.Panic(err)
        }
        err = b.Put([]byte("l"), newBlock.Hash)
        if err != nil {
            log.Panic(err)
        }
        bc.tip = newBlock.Hash

        return nil
    })
}

cli添加send方法

func (cli *CLI) send(from, to string, amount int) {
    bc := NewBlockchain(from)
    defer bc.db.Close()

    tx := NewUTXOTransaction(from, to, amount, bc)
    bc.MineBlock([]*Transaction{tx})
    fmt.Println("Success!")
}

發(fā)送硬幣意味著創(chuàng)建一個(gè)交易并通過挖掘一個(gè)塊將其添加到區(qū)塊鏈。但比特幣不會(huì)立即做到這一點(diǎn)(就像我們一樣)茧跋。相反慰丛,它將所有新事務(wù)放入內(nèi)存池(或mempool)中,并且當(dāng)?shù)V工準(zhǔn)備開采塊時(shí)瘾杭,它將從mempool獲取所有事務(wù)并創(chuàng)建候選塊诅病。交易只有在包含它們的區(qū)塊被挖掘并添加到區(qū)塊鏈時(shí)才會(huì)被確認(rèn)。

現(xiàn)在我們來試試發(fā)幣:

C:\go-worke\src\github.com\study-bitcoin-go>study-bitcoin-go send  -from even -to jim -amount 3
 Mining the block containing "S4t?.U?????H??vWE???[?P?╔???"
 Dig into mine  00000cde90398b754eebe6d7820dab6e6260ae724712b72706846ec6d331fe2c

Success!

分別查詢even粥烁、tom錢包:

C:\go-worke\src\github.com\study-bitcoin-go>study-bitcoin-go getbalance -address even
Balance of 'even': 7

C:\go-worke\src\github.com\study-bitcoin-go>study-bitcoin-go getbalance -address jim
Balance of 'jim': 3

好了目前我們實(shí)現(xiàn)了交易功能贤笆。缺少:

  1. 地址。我們還沒有真實(shí)的讨阻,基于私鑰的地址芥永。
  2. 獎(jiǎng)勵(lì)。采礦塊絕對沒有利潤钝吮!
  3. UTXO設(shè)置埋涧。達(dá)到平衡需要掃描整個(gè)區(qū)塊鏈,當(dāng)區(qū)塊數(shù)量很多時(shí)可能需要很長時(shí)間奇瘦。此外棘催,如果我們想驗(yàn)證以后的交易,可能需要很長時(shí)間耳标。UTXO集旨在解決這些問題并快速處理交易醇坝。
  4. 內(nèi)存池。這是交易在打包成塊之前存儲(chǔ)的地方次坡。在我們當(dāng)前的實(shí)現(xiàn)中呼猪,一個(gè)塊只包含一個(gè)事務(wù)扣泊,而且效率很低逞泄。

后續(xù)降會(huì)實(shí)現(xiàn)地址年枕、錢包崩泡、挖礦獎(jiǎng)勵(lì)蔗喂、網(wǎng)絡(luò)等颠蕴。

5.比特幣交易示例總結(jié)

交易 目的 輸入 輸出 簽名 差額
T0 A轉(zhuǎn)給B 他人向A交易的輸出 B賬號可以使用該交易 A簽確認(rèn) 輸入減去輸出哨免,為交易服務(wù)費(fèi)
T1 B轉(zhuǎn)給C T0的輸出 C賬戶可以使用該交易 B簽名確認(rèn) 輸入減去輸出亚情,為交易服務(wù)費(fèi)
··· X轉(zhuǎn)給Y 他人向X交易的輸出 Y賬戶可以使用該交易 X簽名確認(rèn) 輸入減去輸出摊腋,為交易服務(wù)費(fèi)

這就是簡單交易流程沸版,我們以上代碼并沒有實(shí)現(xiàn)挖礦獎(jiǎng)勵(lì)。后續(xù)將實(shí)現(xiàn)錢包兴蒸,優(yōu)化查詢交易视粮,挖礦獎(jiǎng)勵(lì)、網(wǎng)絡(luò)橙凳。

資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蕾殴,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子岛啸,更是在濱河造成了極大的恐慌钓觉,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坚踩,死亡現(xiàn)場離奇詭異荡灾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瞬铸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門批幌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嗓节,你說我怎么就攤上這事荧缘。” “怎么了拦宣?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵截粗,是天一觀的道長。 經(jīng)常有香客問我鸵隧,道長绸罗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任掰派,我火速辦了婚禮从诲,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘靡羡。我一直安慰自己系洛,他們只是感情好俊性,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著描扯,像睡著了一般定页。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绽诚,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天典徊,我揣著相機(jī)與錄音,去河邊找鬼恩够。 笑死卒落,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蜂桶。 我是一名探鬼主播儡毕,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼扑媚!你這毒婦竟也來了腰湾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤疆股,失蹤者是張志新(化名)和其女友劉穎费坊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旬痹,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡附井,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了唱凯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羡忘。...
    茶點(diǎn)故事閱讀 38,626評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖磕昼,靈堂內(nèi)的尸體忽然破棺而出卷雕,到底是詐尸還是另有隱情,我是刑警寧澤票从,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布漫雕,位于F島的核電站,受9級特大地震影響峰鄙,放射性物質(zhì)發(fā)生泄漏浸间。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一吟榴、第九天 我趴在偏房一處隱蔽的房頂上張望魁蒜。 院中可真熱鬧,春花似錦、人聲如沸兜看。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽细移。三九已至搏予,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間弧轧,已是汗流浹背雪侥。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留精绎,地道東北人速缨。 一個(gè)月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像捺典,于是被迫代替她去往敵國和親鸟廓。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容