iT邦幫忙

2023 iThome 鐵人賽

DAY 18
0
Web 3

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

區塊鏈建立: 區塊鏈 實際運作

  • 分享至 

  • xImage
  •  

區塊鏈 實際運作

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

經過一個禮拜多的努力 區塊鏈總算是成型了

現在只要將其啟動即可

以下分為幾點說明

  1. 區塊鏈啟動程序
  2. 免費交易-觀察內部流動方向
  3. 正常交易-觀察內部流動方向
  4. 查看區塊
  5. 建立多個礦工連線

區塊鏈啟動程序

檔案位置: "./main.go"

func main() {
	DbAddress := "localhost:27017"
	NodeAddr := "127.0.0.1:8080"
	NodeAddresses := []string{}
	Miners := 1

	TransactionChannel := make(chan transaction.Transaction)
	BroadcastTransactionChannel := make(chan transaction.Transaction)
	BlockTransactionChannel := make(chan blockchain.BlockTransaction)
	BroadcastBlockChannel := make(chan blockchain.Block)
	MinersSuccessBlockChannel := make(chan blockchain.Block)
	BlockChannel := make(chan blockchain.Block)
	RefreshBlockChannel := make(chan blockchain.Block)
	MinersBlockChannel := make(chan blockchain.Block)
	CompleteBlockChannel := make(chan blockchain.Block)

	err := utils.InitClient(DbAddress)
	if err != nil {
		return
	}

	go receive_validate_transaction.ReceiveValidateTransaction(TransactionChannel, BroadcastTransactionChannel, BlockTransactionChannel)
	go receive_validate_block.ReceiveValidateBlock(BroadcastBlockChannel, MinersSuccessBlockChannel, BlockChannel, RefreshBlockChannel)
	go refresh_block.RefreshBlock(BlockTransactionChannel, MinersBlockChannel, CompleteBlockChannel)
	go refresh_db.RefreshDb(RefreshBlockChannel, CompleteBlockChannel)
	go miners_leader.MinerLeader(Miners, MinersBlockChannel, MinersSuccessBlockChannel)

	connection.BuildNode(NodeAddr, NodeAddresses, TransactionChannel, BlockChannel, BroadcastTransactionChannel, BroadcastBlockChannel)
}
變數名稱 含意
DbAddress Mongodb 的位置
NodeAddr 基本的節點位置
NodeAddresses 先連結的節點位置

首先對於 mongodb 可以使用 docker 建立容器並端口映射回本機
(方便未來多個節點使用 使用不同端口的資料庫即可)

# 映射回本機27017端口
docker run --name mongodb -p 27017:27017 -d mongo

如何啟動程序

(請先確保運行端口目前是空餘的 而且資料庫已成功運作在指定端口)

//在當前資料夾下的終端上輸入
go run main.go

會看到以上的結果 代表目前已成功建立節點在 127.0.0.1:8080 這個位置上

  • 代表在 BN 程序中 127.0.0.1:8080 的節點
    BN: BuildNode 節點建立程序

免費交易-觀察內部流動方向

隨機地址交易

隨機產一筆公鑰與私鑰對
並且轉送金額給公私鑰相對應的地址

檔案位置: "./api_transaction_submit_free_random_addr.go"

func main() {
	var Amount float64
	Amount = 500

	ConnectAddr := "127.0.0.1:8080"

	pair, err := utils.GenerateKeyPair()
	if err != nil {
		return
	}
	key, err := utils.DecodePublicKey(pair.PublicKey)
	if err != nil {
		return
	}
	addr := utils.GetAddress(conseous.VersionPrefix, key)

	fmt.Println("PublicKey: ", pair.PublicKey)
	fmt.Println("PrivateKey: ", pair.PrivateKey)
	fmt.Println("Address: ", addr)

	clientAPI := api.ClientAPI{ConnectNodeAddr: ConnectAddr}
	err = clientAPI.Connect()
	if err != nil {
		return
	}
	defer func() {
		err := clientAPI.DisConnect()
		if err != nil {
			return
		}
	}()

	err = clientAPI.SubmitFreeTransaction(Amount, addr)
	if err != nil {
		return
	}

}
變數名稱 含意
Amount 轉送的金額
ConnectAddr 連接的礦工節點

如何啟動程序

(請先確保 礦工程序(main.go)正在運行在對應的節點位置 )

//在當前資料夾下的終端上輸入
go run api_transaction_submit_free_random_addr.go

執行完成後便會看到下效果

  • PublicKey 代表 轉帳的帳戶公鑰
  • PrivateKey 代表 轉帳的帳戶私鑰
  • Address 代表 轉帳的帳戶地址

接下來看 礦工程序(main.go) 的窗口

以下為輸出結果按照順序解釋

1. BN (BuildNode 程序) 接收交易

  1. 檢測到連線 127.0.0.1:61784
  2. 獲得訊息 交易內容如下
  3. 判定訊息為 交易
  4. 傳送新交易出去 給 (VT-驗證交易程序)

2. VT (ValidateTransaction 程序) 驗證交易

  1. 獲取交易 交易哈希內容 從 (BN-節點程序)
  2. 獲取最後一個區塊哈希 為 "" 代表第一個區塊
  3. 通過交易驗證 並且發現是 Master 交易
  4. 使用者(Master)有 1000 交易花費 500
  5. 傳送交易出去給 (BN-節點建立 進行廣播交易 與 RB-區塊刷新)

3. BN (BuildNode 程序) 廣播交易

  1. 獲取交易 從 (VT-交易驗證程序)
  2. 傳送出去交易(廣播給相連節點)

4. RB (RefreshBlock 程序) 刷新區塊

  1. 獲取交易 從 (VT-交易驗證程序) 並丟到交易池
  2. 計算好新區塊 並且計算好 MarkleRoot 及相關資訊
  3. 傳送區塊出去 給 (ML-礦工管理者程序)

5. ML (MinersLeader 程序) 礦工管理者

  1. 獲取區塊 從 (RB-刷新區塊程序)
  2. 傳送區塊給計算礦工們 (MS-礦工程序們)

6. MS (Miners 程序) 挖礦程序

  1. 獲取區塊 從 (ML-礦工管理者程序)
  2. 不斷尋找 Nonce 以符合區塊難度
  3. 直到找到符合的 Nonce 傳給 (ML-礦工管理者程序)

7. ML (MinersLeader 程序) 礦工管理者

  1. 獲取成功計算區塊 從 (MS-礦工程序們)
  2. 傳送成功計算區塊 給 (VB-驗證區塊程序)

8. VB (ValidateBlock 程序) 驗證區塊

  1. 獲取區塊 從 (ML-礦工管理者程序)
  2. 傳送區塊 給 (BN-節點建立 進行廣播區塊 與 RDB-資料庫更新)

9. BN (BuildNode 程序) 廣播區塊

  1. 獲取區塊 從 (VB-驗證區塊程序)
  2. 傳送出去區塊(廣播給相連節點)

10. RDB (RefreshDB 程序) 更新資料庫

  1. 獲取區塊 從 (VB-驗證區塊程序)
  2. 檢查是否為創世區塊
  3. 更新 區塊資訊 與 交易資訊 在資料庫
  4. 傳送區塊 給 (RB-刷新區塊程序)

11. RB (RefreshBlcok 程序) 更新區塊

  1. 獲取區塊 從 (RDB-刷新資料庫程序)
  2. 傳送新區塊 給 (ML-礦工管理者程序)

12. ML (MinersLeader 程序) 礦工管理者

  1. 獲取區塊 從 (RB刷新區塊程序)
  2. 傳送區塊 給 (MS-礦工程序們)

13. MS (Miners 程序) 挖礦程序

  1. 獲取成功計算區塊 從 (ML-礦工管理者程序)
  2. 檢查區塊難度 與 區塊交易大小
  3. 發現交易量僅為1(礦工運算證明交易) 停止運算

透過指定的地址 轉送免費金額給地址

檔案位置: "./api_transaction_submit_free.go"

func main() {
	var Amount float64
	Amount = 0

	var Addr string
	Addr = "Address"

	ConnectAddr := "127.0.0.1:8080"

	clientAPI := api.ClientAPI{ConnectNodeAddr: ConnectAddr}
	err := clientAPI.Connect()
	if err != nil {
		return
	}
	defer func() {
		err := clientAPI.DisConnect()
		if err != nil {
			return
		}
	}()

	err = clientAPI.SubmitFreeTransaction(Amount, Addr)
	if err != nil {
		return
	}
}
變數名稱 含意
Amount 轉送的金額
Addr 轉送的地址
ConnectAddr 連接的礦工節點

基本上 交易流程與上方都相同故省略

正常交易-觀察內部流動方向

正常交易
由上方所產生的 公私鑰與地址 轉金額給自己

檔案位置: "./api_transaction_submit.go"

func main() {
	var UTXOHash string
	var Index int
	var Address string
	var Amount float64
	var Fee float64
	var PublicKey string
	var PrivateKey string

	UTXOHash = "UTXOHash"
	Index = 0
	Address = "Address"
	Amount = 0
	Fee = 0
	PublicKey = "PublicKey"
	PrivateKey = "PrivateKey"

	ConnectAddr := "127.0.0.1:8080"

	f := transaction.From{UTXOHash: UTXOHash, Index: Index}
	t := transaction.To{Address: Address, Amount: Amount}

	ts := transaction.Transaction{From: []transaction.From{f}, To: []transaction.To{t}, Fee: Fee, PublicKey: PublicKey}

	privateKey, err := utils.DecodePrivateKey(PrivateKey)
	if err != nil {
		return
	}
	strVal, err := ts.ToString()
	if err != nil {
		return
	}
	signature, err := utils.Signature(strVal, privateKey)
	if err != nil {
		return
	}
	ts.Signature = signature
	ts.TransactionHash = utils.HashSHA256(strVal)
	fmt.Println("TransactionHash: ", ts.TransactionHash)
	fmt.Println()
	fmt.Println("From: ")
	fmt.Println("PublicKey: ", PublicKey)
	fmt.Println("PrivateKey: ", PrivateKey)
	fmt.Println("UTXOHash: ", UTXOHash)
	fmt.Println("Index: ", Index)
	fmt.Println()
	fmt.Println("To: ")
	fmt.Println("Address: ", Address)
	fmt.Println("Amount: ", Amount)
	fmt.Println()
	fmt.Println("Fee: ", Fee)
	fmt.Println()
	fmt.Println("Signature: ", signature)

	clientAPI := api.ClientAPI{ConnectNodeAddr: ConnectAddr}
	err = clientAPI.Connect()
	if err != nil {
		return
	}
	defer func() {
		err := clientAPI.DisConnect()
		if err != nil {
			return
		}
	}()

	err = clientAPI.SubmitTransaction(ts.From, ts.To, ts.Fee, ts.PublicKey, ts.Signature)
	if err != nil {
		fmt.Println(err)
		return
	}
}
變數名稱 含意
UTXOHash 上次交易的交易哈希
Index 上次交易中接收的次序
Address 交易接收地址
Amount 交易接收金額
Fee 交易的小費
PublicKey 傳輸者的公鑰
PrivateKey 傳輸者的私鑰
ConnectAddr 連接的礦工節點

如何啟動程序

(請先確保 礦工程序(main.go)正在運行在對應的節點位置 )

//在當前資料夾下的終端上輸入
go run api_transaction_submit.go
// 以上方第一次免費的交易為例
UTXOHash="a2fff3aec37712009693ef269ff04b716a3248290819d7a59f8b49ff1008a48a"
Address="1BFTNxqbCZzcV9xS6SUwA23YAt"
Index=0
Amount=150
Fee=50
PublicKey="BI/0HLw2PUacr2jIVy7U3G0mFlV2kDcnBSHv+JjcGFCttexgvZzneN+c6YadzqLIk6nSLjrMQiGhAAWlexjh10o="
PrivateKey="7/+xOoENHZ89xlZVkkMvJZ0ltEdHrLTYPCCFsbASg7I="

會看到以下的輸出結果

  • TransactionHash 交易哈希
  • PublicKey 公鑰
  • PrivateKey 私鑰(此處進用來簽名 並不會傳出去)
  • From 交易發起資訊
    • UTXOHash 前筆交易的交易哈希值
    • Index 交易索引
  • TO 交易接收資訊
    • Address 接收金額位置
    • Amount 接收金額
  • Fee 交易手續費
  • Signature 交易簽名

接下來看 礦工程序(main.go) 的窗口

以下為輸出結果按照順序解釋

1. BN (BuildNode 程序) 接收交易

  1. 發現節點連線
  2. 獲得傳輸內容 並判斷為交易
  3. 傳送交易 給 (VT-驗證交易)

2. VT (ValidateTransaction 程序) 驗證交易

  1. 獲取交易 從 (BN-節點建立)
  2. 驗證交易 並且通過簽名驗證
  3. 擁有 500 花費 200 (轉帳:150+手續費:50)
  4. 傳送交易 給 (BN-節點建立(廣播交易) 與 RB-刷新區塊)

3. BN (BuildNode 程序) 廣播交易

  1. 獲取交易 從 (VT-驗證交易)
  2. 廣播交易

4. RB (RefreshBlock 程序) 刷新區塊

  1. 獲取交易 從 (VT-驗證交易)
  2. 建立區塊
  3. 傳送區塊 給 (ML-礦工管理者)

5. ML (MinersLeader 程序) 礦工管理者

  1. 獲取區塊 從 (RB-刷新區塊)
  2. 傳送區塊 給 (MS-礦工們)

6. MS (Miners 程序) 挖礦程序


  1. 獲取區塊 從 (ML-礦工管理者)
  2. 獲取區塊基本資訊
  3. 計算區塊 並 傳給 (ML-礦工管理者)

7. ML (MinersLeader 程序) 礦工管理者

  1. 獲取完成區塊 從 (MS-礦工們)
  2. 傳送完成區塊 給 (VB-驗證區塊)

8. VB (ValidateBlock 程序) 驗證區塊

  1. 獲取完成區塊 從 (ML-礦工管理者)
  2. 傳送完成區塊 給 (BN-節點建立 與 RDB-資料庫更新)

9. BN (BuildNode 程序) 廣播區塊

  1. 獲取完成區塊 從 (VB-驗證區塊)
  2. 廣播完成區塊

10. RDB (RefreshDB 程序) 更新資料庫

  1. 獲取完成區塊 從 (VB-驗證區塊)
  2. 驗證內容 判斷區塊位置
  3. 更新 UTXO 與 區塊 資料庫
  4. 將區塊傳送 給 (RB-刷新區塊)

11. RB (RefreshBlcok 程序) 更新區塊

  1. 獲取區塊 從 (RDB-刷新資料庫程序)
  2. 傳送新區塊 給 (ML-礦工管理者程序)

12. ML (MinersLeader 程序) 礦工管理者

  1. 獲取區塊 從 (RB刷新區塊程序)
  2. 傳送區塊 給 (MS-礦工程序們)

13. MS (Miners 程序) 挖礦程序

  1. 獲取區塊 從 (ML-礦工管理者程序)
  2. 檢查區塊難度 與 區塊交易大小
  3. 發現交易量僅為1(礦工運算證明交易) 停止運算

查看區塊

1. 直接查看最後區塊


首先 區塊哈希 與觀察紀錄相同

再來 區塊標頭 上 也與上方記錄相同(Nonce=27)等等

最後看到 區塊交易
第一筆 為礦工紀錄交易 證明該個區塊是這名礦工計算的
(此區塊的獎勵也歸這名礦工擁有)
第二筆 為上方提交交易 交易哈希也符合
(資訊也符合 金額 150 小費 50)

2. 根據哈希值查詢區塊

查詢 BlockHash 等於 上筆區塊哈希

與上方或的相同效果

3. 根據區塊高度查詢區塊

查詢 區塊高度 為 0 的第一個區塊


確實為創世區塊資訊

首先在 區塊哈希 上 符合上方紀錄
BlockMeta 亦有留下 我對創世區塊的留言

另外在 區塊標頭 上 也符合紀錄 (Nonce=2)

最後在 交易紀錄 上 就是最前一面第一筆 免費獲得500的紀錄

建立多個礦工連線

首先透過 docker 建立一個不同端口的 mongodb

//映射為本機27018端口
docker run --name mongodb -p 27018:27017 -d mongo

// main.go 內容修正
DbAddress := "localhost:27018"
NodeAddr := "127.0.0.1:8081"
NodeAddresses := []string{"127.0.0.1:8080"}

接下執行 go run main.go
(確保先前 main.go 程序還在執行)

就會得到以上結果

  1. 先試圖向 127.0.0.1:8080 拿取資料
  2. 透過獲取區塊 更新區塊資料庫 並且創建UTXO資料
  3. 不斷向前獲取 直到達到創世區塊
  4. 結束初始化資料庫工作
  5. 連接節點 並開始工作

接下來 不論是在 區塊獲取 或是 提交交易 都可以獲得相同結果

// 例如 api_block_get_by_block_height 進行修正 
ConnectAddr := "127.0.0.1:8081"


都可以或的相同的資訊 達到真實的去中心化

其他方式也都可以達到相同的結果!!!

結言

感覺今天講述的範圍很廣

如果想實際體驗一次 可以在GitHub上Clone專案在本地跑一次

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

基本上如何執行在 README.md 也寫得很清晰了

其實還有很多可以去嘗試跟玩的!!!

希望透過這篇能夠理解

  1. 如何使用這程序
  2. 關於整個區塊鏈服務 內部邏輯運作
  3. 從交易到區塊的流程
  4. 節點連接達到去中心化

下回預告

必須說 這個區塊鏈程序 我很有成就感
從一開始規劃 到後續實現

真的一度認為這要失敗了

尤其對於 Go 中 Goroutine 與 Channel 應用
也是透過這次我才稍微學會撰寫平行程序

而且也是透過實踐 確切理解
去中心化、共識機制、分支處理、資料庫處理、交易....

結束了整個區塊鏈的製造
想目前對於區塊鏈的模樣 運作細節 網路架構 傳輸方式 都非常孰悉!!!

不過呢? 目前區塊鏈最常見的應用 還沒碰觸到呢? 是時候邁入應用了吧?
那當然就是 "智能合約"

接下來就要趕緊進入 智能合約的撰寫與應用

下回 "智能合約開發: Solidity 與 智能合約基礎介紹"


上一篇
區塊鏈建立: 區塊鏈 API 相關接口
下一篇
智能合約開發: Solidity 與 智能合約基礎介紹
系列文
從 區塊鏈 到 去中心化應用程式(DApp)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言