iT邦幫忙

2023 iThome 鐵人賽

DAY 15
0
Web 3

從 區塊鏈 到 去中心化應用程式(DApp)系列 第 15

區塊鏈建立: 區塊運算 之 區塊的流向

  • 分享至 

  • xImage
  •  

區塊運算 之 區塊的流向

專案 GitHub 位置: https://github.com/weiawesome/block-chain_go

在區塊運算當中 還有一環非常重要的環節
便是 區塊的流向

新區塊 獲得的方式

  • 收到從其他節點廣播的新區塊
  • 礦工自己運算的新區塊

新區塊會影響的範圍

  • 區塊資料庫 與 UTXO資料庫 內容更動
  • 當前計算區塊 與 交易池內容變化

因此這篇主要以分幾點為敘述

  1. 接收與驗證區塊
  2. 資料庫更新區塊內容
  3. 更新交易池與當前運算區塊

完成的範圍便是 接受驗證區塊刷新資料庫刷新區塊

接受驗證區塊

Channel 名稱 Channel 類型 地點
BlockChannel 廣播系統
MinersSuccessBlockChannel 礦工管理者
BroadcastBlockChannel 廣播系統
RefreshBlockChannel 刷新資料庫

刷新資料庫

Channel 名稱 Channel 類型 地點
RefreshBlockChannel 接受驗證區塊
CompleteBlockChannel 刷新區塊

刷新區塊

Channel 名稱 Channel 類型 地點
BlockTransactionChannel 接受驗證交易
CompleteBlockChannel 刷新資料庫
MinersBlockChannel 礦工管理者

接收與驗證區塊

檔案位置: "./service/receive_validate_block/receive_validate_block.go"

func ReceiveValidateBlock(BroadcastBlockChannel chan blockchain.Block, MinersSuccessBlockChannel chan blockchain.Block, BlockChannel chan blockchain.Block, RefreshBlockChannel chan blockchain.Block) {
	for {
		select {
		case mb := <-MinersSuccessBlockChannel:
			BroadcastBlockChannel <- mb
			RefreshBlockChannel <- mb
		case b := <-BlockChannel:
			_, err := block.GetBlock(b.BlockHash)
			if err != nil {
				if b.CheckDifficulty() {
					tmpBlockHash := b.BlockHash
					b.ComputeBlockHash()
					if tmpBlockHash == b.BlockHash {
						tmpMarkleRoot := b.BlockTop.MarkleRoot
						b.ComputeMarkleRoot()
						if tmpMarkleRoot == b.BlockTop.MarkleRoot {
							BroadcastBlockChannel <- b
							RefreshBlockChannel <- b
						}
					}
				}	
			}
		default:
			continue
		}
	}
}

首先對於獲取新區塊的方式 分為兩種渠道(Channel)

  1. 礦工自己運算獲得的區塊
    由於是自己運算的 因此不浪費時間運算 而是直接廣播與
  2. 從其他節點廣播獲知的
    首先檢查資料庫是否存在該筆區塊 若已存在則不計算該區塊
    若無則檢查區塊哈希、Markle root、區塊難度等等 再廣播與設定資料庫

資料庫更新區塊內容

檔案位置: "./service/refresh_db/refresh_db.go"

func RefreshDb(RefreshBlockChannel chan blockchain.Block, CompleteBlockChannel chan blockchain.Block) {
	for {
		select {
		case rb := <-RefreshBlockChannel:
			if rb.BlockTop.PreviousHash == conseous.GenesisBlockPreviousHash {
				_, err := block_control.GetLastBlock()
				if err != nil {
					err := block_control.SetLastBlock(rb.BlockHash)
					if err != nil {
						continue
					}
				}
				err = block_control.SetCandidateBlock(rb.BlockHash)
				if err != nil {
					continue
				}
				err = block.SetBlock(rb)
				if err != nil {
					continue
				}
				for _, transaction := range rb.BlockTransactions {
					err := utxo.SetUTXO(transaction)
					if err != nil {
						continue
					}
				}
				CompleteBlockChannel <- rb
				continue
			} else {
				_, err := block.GetBlock(rb.BlockTop.PreviousHash)
				if err != nil {
					continue
				}
			}
			lastBlockHash, err := block_control.GetLastBlock()
			if err != nil {
				continue
			}
			lastBlock, err := block.GetBlock(lastBlockHash)
			if err != nil {
				continue
			}
			if rb.BlockTop.BlockHeight > lastBlock.BlockTop.BlockHeight-conseous.BlockChecked {
				err := block_control.SetCandidateBlock(rb.BlockHash)
				if err != nil {
					continue
				}
				for _, transaction := range rb.BlockTransactions {
					err := utxo.SetUTXO(transaction)
					if err != nil {
						continue
					}
				}
				if rb.BlockTop.BlockHeight > lastBlock.BlockTop.BlockHeight {
					err := block_control.SetLastBlock(rb.BlockHash)
					if err != nil {
						continue
					}
					lastBlock = rb
					if lastBlock.BlockTop.BlockHeight-conseous.BlockChecked >= conseous.GenesisBlockHeight {
						tmpBlock := lastBlock
						for i := 0; i < conseous.BlockChecked; i++ {
							tmpBlock, err = block.GetBlock(tmpBlock.BlockTop.PreviousHash)
							if err != nil {
								continue
							}
						}
						err := block_control.DeleteCandidateBlock(tmpBlock.BlockHash)
						if err != nil {
							continue
						}
						candidateBlocks, err := block_control.GetCandidateBlock()
						if err != nil {
							continue
						}
						for _, candidateBlock := range candidateBlocks {
							b, err := block.GetBlock(candidateBlock)
							if err != nil {
								return
							}
							if b.BlockTop.BlockHeight == tmpBlock.BlockTop.BlockHeight+1 {
								if b.BlockTop.PreviousHash != tmpBlock.BlockHash {
									err := block_control.DeleteCandidateBlock(b.BlockHash)
									if err != nil {
										continue
									}
									for _, transaction := range b.BlockTransactions {
										err := utxo.ReverseUTXO(transaction)
										if err != nil {
											continue
										}
									}
									err = block.DeleteBlock(b.BlockHash)
									if err != nil {
										continue
									}
								}
							} else if b.BlockTop.BlockHeight == tmpBlock.BlockTop.BlockHeight+1 {
								err := block_control.DeleteCandidateBlock(b.BlockHash)
								if err != nil {
									continue
								}
								for _, transaction := range b.BlockTransactions {
									err := utxo.ReverseUTXO(transaction)
									if err != nil {
										continue
									}
								}
								err = block.DeleteBlock(b.BlockHash)
								if err != nil {
									continue
								}
							}

						}
						err = block_control.SetCheckedBlock(tmpBlock.BlockHash)
						if err != nil {
							continue
						}
					}

				}
				CompleteBlockChannel <- rb
			}

		default:
			continue
		}
	}
}

基本上 就是一進一出
會檢查該筆區塊 在該個節點的位置

並適當處理分支的問題

以上圖為例
當新增區塊時 會先確認該區塊的位置

當有個區塊已經達到多區塊認證(數量多少為協議決定)
則該 該個區塊則被視為已被確認
接下來會刪除掉非此塊分支的區塊

圖片順序分別為

  1. 首先原本區塊鏈模樣
  2. 新增黃色區塊
  3. 更改確認區塊 同時刪除分支

這便是區塊鏈對於分支的處理

最後再將資料庫做更正與處理
若是該筆區塊是最後區塊則再傳遞給 更新區塊

更新交易池與當前運算區塊

檔案位置: "./service/service/refresh_block.go"

func RefreshBlock(BlockTransactionChannel chan blockchain.BlockTransaction, MinersBlockChannel chan blockchain.Block, CompleteBlockChannel chan blockchain.Block) {
	TransactionPool := make(utils.PriorityQueue, 0)
	var TargetBlock blockchain.Block
	key, err := utils.DecodePublicKey(utils.GetPublicKey())
	if err != nil {
		return
	}
	address := utils.GetAddress(conseous.VersionPrefix, key)
	minerTransaction, err := transactionUtils.MinerTransaction(address)
	if err != nil {
		return
	}
	for {
		select {
		case bt := <-BlockTransactionChannel:
			heap.Push(&TransactionPool, &bt)
			var tmpBlock blockchain.Block
			tmpBlock.BlockTransactions = append(tmpBlock.BlockTransactions, minerTransaction)
			tmpTransactionPool := TransactionPool
			for tmpTransactionPool.Len() > 0 {
				t := heap.Pop(&tmpTransactionPool).(*blockchain.BlockTransaction)
				tmpBlock.BlockTransactions = append(tmpBlock.BlockTransactions, *t)
				if unsafe.Sizeof(tmpBlock) > conseous.BlockSize {
					tmpBlock.BlockTransactions = tmpBlock.BlockTransactions[:len(tmpBlock.BlockTransactions)-1]
					break
				}
			}
			tmpBlock.ComputeMarkleRoot()
			if tmpBlock.BlockTop.MarkleRoot != TargetBlock.BlockTop.MarkleRoot {
				TargetBlock = tmpBlock
				TargetBlock.BlockTop.Version = conseous.Version
				TargetBlock.BlockTop.TimeStamp = time.Now().Unix()
				blockHash, err := block_control.GetLastBlock()

				if err != nil || blockHash == conseous.GenesisBlockPreviousHash {
					blockHash = conseous.GenesisBlockPreviousHash
					TargetBlock.BlockMeta = blockchain.BlockMeta{Content: conseous.GenesisBlockContent}
					TargetBlock.BlockTop.BlockHeight = conseous.GenesisBlockHeight
					TargetBlock.BlockTop.Difficulty = conseous.GenesisBlockDifficulty
				} else {
					b, err := block.GetBlock(blockHash)
					fmt.Println(b.BlockHash, err)
					if err != nil {
						continue
					}
					TargetBlock.BlockTop.BlockHeight = b.BlockTop.BlockHeight + 1
					TargetBlock.BlockTop.Difficulty = b.BlockTop.Difficulty
					if TargetBlock.BlockTop.BlockHeight%conseous.DifficultyCycle == 0 {
						speed, err := block.CheckGenerateSpeed(TargetBlock.BlockTop.BlockHeight)
						if err == nil {
							if speed {
								if TargetBlock.BlockTop.Difficulty > conseous.DifficultyLower {
									TargetBlock.BlockTop.Difficulty -= 1
								}
							} else {
								if TargetBlock.BlockTop.Difficulty < conseous.DifficultyUpper {
									TargetBlock.BlockTop.Difficulty += 1
								}
							}
						}
					}
				}
				TargetBlock.BlockTop.PreviousHash = blockHash
				MinersBlockChannel <- TargetBlock
			}
		case cb := <-CompleteBlockChannel:
			TargetBlock = blockchain.Block{}
			TargetBlock.BlockTop.Version = conseous.Version
			TargetBlock.BlockTop.TimeStamp = time.Now().Unix()
			TargetBlock.BlockTop.PreviousHash = cb.BlockHash
			TargetBlock.BlockTransactions = append(TargetBlock.BlockTransactions, minerTransaction)
			tmpTransactionPool := make(utils.PriorityQueue, 0)
			for TransactionPool.Len() > 0 {
				t := heap.Pop(&TransactionPool).(*blockchain.BlockTransaction)
				flag := false
				for _, transaction := range cb.BlockTransactions {
					if transaction.TransactionHash == t.TransactionHash {
						flag = true
					}
				}
				if !flag {
					heap.Push(&tmpTransactionPool, t)
					TargetBlock.BlockTransactions = append(TargetBlock.BlockTransactions, *t)
					if unsafe.Sizeof(TargetBlock) > conseous.BlockSize {
						TargetBlock.BlockTransactions = TargetBlock.BlockTransactions[:len(TargetBlock.BlockTransactions)-1]
					}
				}
			}
			TargetBlock.ComputeMarkleRoot()
			TransactionPool = tmpTransactionPool
			MinersBlockChannel <- TargetBlock
		default:
			continue
		}
	}
}

分為兩種進入的渠道(Channel)

第一種是新交易進入的方式 這裡便不贅述
第二種則是透過更新資料庫後結果傳過來的新區塊

  1. 清空區塊內容
  2. 設定區塊基本資訊(版本、前區塊哈希、礦工交易...
  3. 重新檢查交易池 以重新塑造區塊
  4. 更新交易池 並且傳給礦工管理者 新的區塊

結言

理解 區塊的流向 是很重要的一環

怎麼獲得? 影響的範圍是甚麼?

對於這些基礎的疑惑 有基礎的概念
方能找出對應的解決方式

而且對於整個區塊鏈的運行更能理解其內容與機制

希望透過這篇能夠知曉

  1. 區塊在計算(挖礦)過程的流向
  2. 區塊鏈對於分支的處理
  3. 更新資料庫、交易池、計算區塊

下回預告

好的 今日也解決 關於區塊的流向問題

因此目前對於 區塊運算當中
已有 區塊 與 交易 的資訊

那接下來是時候 該輪到挖礦的環節了吧

下回 "區塊鏈建立: 區塊運算 之 礦工與他們的領導者"


上一篇
區塊鏈建立: 區塊運算 之 交易的流向
下一篇
區塊鏈建立: 區塊運算 之 礦工與他們的領導者
系列文
從 區塊鏈 到 去中心化應用程式(DApp)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言