<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 前言 前面為了轉賬,需要迭代整個區塊鏈,查找未花費的輸出,這要求發起交易者必須運行一個全節點,截止 2017 年 9 月 18 日,在比特幣中已經有 485,860 個塊,整個數據庫所需磁盤空間超過 140 Gb。這是非常困難的。 整個問題的解決方案是有一個僅有未花費輸出的索引,這就是 UTXO 集要做的事情:這是一個從所有區塊鏈交易中構建(對區塊進行迭代,但是只須在創建區塊鏈時候做一次)而來的緩存,然后用它來計算余額和驗證新的交易。截止 2017 年 9 月,UTXO 集大概有 2.7 Gb,遠小于整個節點。 # 順便實現下挖礦獎勵 我們的區塊鏈由交易發起者自行完成挖礦,所以獎勵給到交易發起者即可。 挖礦獎勵,實際上就是一筆 coinbase 交易。 ~~~ func (cli *CLI) send(from, to string, amount int) { ... bc := NewBlockchain() UTXOSet := UTXOSet{bc} defer bc.db.Close() tx := NewUTXOTransaction(from, to, amount, &UTXOSet) cbTx := NewCoinbaseTX(from, "")//創建一個coninbase交易,給from發送一個固定數量的獎勵。 txs := []*Transaction{cbTx, tx}//將coninbase交易和實際交易打包到一起 newBlock := bc.MineBlock(txs)//挖礦 fmt.Println("交易成功!") } ~~~ >bitcoin的挖礦由專職礦工完成。交易發起者將交易丟進交易池中,礦工從交易池中取出一部分交易,打包挖礦,獲得獎勵。獎勵幣數量是不固定的。 # 實現UTXO UTXO集定義 ~~~ type UTXOSet struct { Blockchain *Blockchain } ~~~ UTXO元素是一個區塊鏈Blockchain的引用 ## 取得UTXO并存儲到新的bucket中 ~~~ func (u UTXOSet) Reindex() { db := u.Blockchain.Db//與區塊鏈使用相同的數據庫 bucketName := []byte(utxoBucket)//新的bucket err := db.Update(func(tx *bolt.Tx) error { err := tx.DeleteBucket(bucketName)//如果存在,則刪除 _, err = tx.CreateBucket(bucketName)//創建新的bucket }) UTXO := u.Blockchain.FindUTXO()//獲得所有未花費輸出 err = db.Update(func(tx *bolt.Tx) error { b := tx.Bucket(bucketName) for txID, outs := range UTXO { key, err := hex.DecodeString(txID) err = b.Put(key, outs.Serialize())//將交易ID-輸出存儲到bucket } }) } ~~~ 這個方法僅僅在區塊鏈新建完成后,唯一一次被調用:當一個新的區塊鏈被創建以后,就會立刻進行重建索引。 ~~~ func (cli *CLI) createBlockchain(address string) { ... bc := CreateBlockchain(address) defer bc.db.Close() UTXOSet := UTXOSet{bc} UTXOSet.Reindex() ... } ~~~ ## 轉賬的新方式~~~ func (u UTXOSet) FindSpendableOutputs(pubkeyHash []byte, amount int) (int, map[string][]int) { unspentOutputs := make(map[string][]int) accumulated := 0 db := u.Blockchain.Db err := db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(utxoBucket))//從bucket中讀取UTXO集 c := b.Cursor() for k, v := c.First(); k != nil; k, v = c.Next() {//循環UTXO集 txID := hex.EncodeToString(k) outs := DeserializeOutputs(v) for outIdx, out := range outs.Outputs { if out.IsLockedWithKey(pubkeyHash) && accumulated < amount { accumulated += out.Value unspentOutputs[txID] = append(unspentOutputs[txID], outIdx) } } } }) return accumulated, unspentOutputs } ~~~ ## 查詢余額的新方式 ~~~ func (u UTXOSet) FindUTXO(pubKeyHash []byte) []TXOutput { var UTXOs []TXOutput db := u.Blockchain.Db err := db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(utxoBucket))//從bucket中讀取UTXO集 c := b.Cursor() for k, v := c.First(); k != nil; k, v = c.Next() {//循環UTXO集 outs := DeserializeOutputs(v) for _, out := range outs.Outputs { if out.IsLockedWithKey(pubKeyHash) { UTXOs = append(UTXOs, out) } } } return nil }) return UTXOs } ~~~ ## 同步機制 當挖出一個新塊時,應該更新 UTXO 集。 更新意味著移除已花費輸出,并從新挖出來的交易中加入未花費輸出。 ~~~ ~~~ func (u UTXOSet) Update(block *Block) {//block為挖出的新區塊 db := u.Blockchain.db err := db.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(utxoBucket)) for _, tx := range block.Transactions {//迭代新區塊里面的所有交易 if tx.IsCoinbase() == false {//coinbase交易不更新到UTXO集中?那么挖礦獎勵金怎么辦? for _, vin := range tx.Vin { updatedOuts := TXOutputs{}//交易ID為vin.Txid的新的UTXO,將替換原來數據庫中交易ID為vin.Txid的UTXO outsBytes := b.Get(vin.Txid)//從數據庫讀取的UTXO集中獲得被包含到輸入的輸出交易ID,一個vin中只有一個輸出索引 outs := DeserializeOutputs(outsBytes) for outIdx, out := range outs.Outputs {//迭代數據庫該交易的所有索引的輸出。輸出集合可能有多個索引對應的輸出 if outIdx != vin.Vout { //如果某條索引對于的輸出沒有被包含在輸入中,加入到新的最終輸出集合中。 //注意,包含到輸入中輸出最小單位是輸出的某條索引對應的輸出(索引從0開始) updatedOuts.Outputs = append(updatedOuts.Outputs, out)//加入到新的已經花費的輸出組中 } } if len(updatedOuts.Outputs) == 0 { //一個交易ID為vin.Txid的交易完成后,交易的輸出索引對應的輸出將被移除,如果結果是,該交易不再包含任何輸出, //那么這筆交易的輸出應該被直接從UTXO數據庫中移除(UTXO鏈沒有必要留著空的節點)。 err := b.Delete(vin.Txid) } else { //如果交易ID為vin.Txid的交易在完成后,仍然有輸出,則更新到數據庫,替換該交易原來的UTXO集合 err := b.Put(vin.Txid, updatedOuts.Serialize()) } } } //當然,交易將產生新的輸出,直接加入到UTXO中即可 newOutputs := TXOutputs{} for _, out := range tx.Vout {//迭代Vout,獲得輸出索引對應的輸出 newOutputs.Outputs = append(newOutputs.Outputs, out) } err := b.Put(tx.ID, newOutputs.Serialize())//tx.ID為當前交易的ID } }) } ~~~ # Merkle樹實現 上如上面所提到的,完整的比特幣數據庫(也就是區塊鏈)需要超過 140 Gb 的磁盤空間。因為比特幣的去中心化特性,網絡中的每個節點必須是獨立,自給自足的,也就是每個節點必須存儲一個區塊鏈的完整副本。隨著越來越多的人使用比特幣,這條規則變得越來越難以遵守:因為不太可能每個人都去運行一個全節點。并且,由于節點是網絡中的完全參與者,它們負有相關責任:節點必須驗證交易和區塊。另外,要想與其他節點交互和下載新塊,也有一定的網絡流量需求。 在中本聰的[比特幣原始論文](https://link.jianshu.com/?t=https://bitcoin.org/bitcoin.pdf)中,對這個問題也有一個解決方案:簡易支付驗證(Simplified Payment Verification, SPV)。SPV 是比特幣輕節點,它不需要下載整個區塊鏈,也**不需要驗證區塊和交易**。相反,它會在區塊鏈查找交易(為了驗證支付),并且需要連接到一個全節點來檢索必要的數據。這個機制允許在全網只運行一個全節點的情況下有多個輕節點(輕錢包)。 為了實現 SPV,**需要有一個方式來檢查是否一個區塊包含了某筆交易,而無須下載整個區塊。這就是 Merkle 樹所要完成的事情。** 比特幣用 Merkle 樹來獲取交易哈希,哈希被保存在區塊頭中,并會用于工作量證明系統。 Merkle 樹的好處就是一個節點可以在不下載整個塊的情況下,驗證是否包含某筆交易。并且這些只需要一個當前交易哈希,一個 Merkle 樹根哈希和一個 Merkle 路徑(向全網請求獲得路徑),然后本地計算,即可檢查是否包含了某筆交易。 # 運行檢驗 ## 轉賬 ![](https://img.kancloud.cn/20/91/20912871fa21fccf1fb65cc58c602945_1580x117.png) 轉賬結果: ![](https://img.kancloud.cn/d6/f9/d6f9da32dcf17f2c91bcb39031299508_1108x229.png) 之所以轉賬后from的幣數量是17,是因為轉賬挖礦獎勵10幣,加上轉賬余額7幣,一共17個幣。 ## 打印區塊鏈 ![](https://img.kancloud.cn/89/a0/89a06f821fd71033c1fc13550055993f_1962x1223.png) # 優化Findtranaction 在很多場合需要根據交易ID,查找交易,函數Findtrasaction功能即如此。 為此,我們將<tx.ID,block.Hash>保存到數據庫,這樣,我們通過數據庫表utxoBlockBucket的key(tx.ID)可以查詢到block的ID,然后通過blocksBucket的Get,可以快速獲得交易。(原來是通過迭代blockchain來完成,效率很低)。這樣,也為后面輕節點通過網絡請求得到交易留下優化空間。 優化之后,節點仍然需要下載整個區塊鏈,才能查詢到交易。 ## 表 const utxoBlockBucket?=?"chainstate\_blockid2tx" ## 修改Reindex ![](https://img.kancloud.cn/e3/ed/e3ed0a7ea3cc1435d3eaeb637f7bcdf3_1642x978.png) 將UTXO和UTXOBlock都寫入數據庫 ## 修改同步函數 ![](https://img.kancloud.cn/74/2c/742c57b30cdcf7356ad9d0acd51a3a71_908x286.png) 更新UTXO的同時,同步UTXOBlock ## 修改FindtransactionForUTXO ![](https://img.kancloud.cn/37/3e/373ef208664da14d14b1017fc113af81_1337x1473.png) ## 修改其它代碼
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看