# 部署 Hyperledger Fabric sample ## 環境準備 在建立 Docker-based Fabric 網路環境之前,要安裝 pre-required 套件。官網上沒有特別說 Linux 需要的環境,但在[官方文檔](https://hyperledger-fabric.readthedocs.io/en/latest/prereqs.html#linux-ubuntu-debian-based-distro) 針對 Windows 用戶的環境有提到 "Next you will need to install a Linux distribution such as Ubuntu-20.04 and make sure ...",所以就去安裝 Ubuntu-20.04。 ### 安裝 Ubuntu 20.04 到[官方網站](https://releases.ubuntu.com/focal/)下載 Ubuntu 20.04.6 LTS (Focal Fossa),再用 virtual box 打開。 ![image](https://hackmd.io/_uploads/ryR7gUwu1l.png) 預設密碼參考[官網](https://ubuntu.com/tutorials/how-to-run-ubuntu-desktop-on-a-virtual-machine-using-virtualbox) ![image](https://hackmd.io/_uploads/SyZ8Evv_ke.png) ### Terminal 無法正常開啟 原本我的 terminal 都點不開,也不會報錯。參考這篇[文章](https://askubuntu.com/questions/1442350/terminal-not-launching-in-ubuntu-22-04),去設定把語言改掉,並重新啟動。 ![image](https://hackmd.io/_uploads/rJHuTvwOkl.png) ![image](https://hackmd.io/_uploads/BkNF6DD_1g.png) ![image](https://hackmd.io/_uploads/Hk4cpvDO1g.png) terminal 就可以正常開啟了,連中文亂碼也解決了 ovo。 ![image](https://hackmd.io/_uploads/SkuaTPD_1x.png) ### 使用者提權 如果使用 sudo 把自己提權出現錯誤 "... is not in the sudoers file" ,可以參考此[文章](https://prathapreddy-mudium.medium.com/vboxuser-is-not-in-the-sudoers-file-this-incident-will-be-reported-enable-sudo-in-ubuntu-resolved-305e7988c6bc),先使用 `su` 指令切去 root 用戶,密碼是你原先帳戶的密碼。 安裝 sudo ``` apt-get install sudo -y ``` 把原本的使用者名稱加入 sudo 群組 ``` adduser vboxuser sudo ``` ![image](https://hackmd.io/_uploads/ByFu-OwOJe.png) 修改 /etc/sudoer 文件 ![image](https://hackmd.io/_uploads/ryQlfuPuyg.png) 成功 ![image](https://hackmd.io/_uploads/HJ0Vzuv_kx.png) ### 雙向複製貼上 可以參考[這篇文章](https://medium.com/%E8%8A%B1%E5%93%A5%E7%9A%84%E5%A5%87%E5%B9%BB%E6%97%85%E7%A8%8B/%E8%A7%A3%E6%B1%BAvirtualbox%E7%84%A1%E6%B3%95%E9%9B%99%E5%90%91%E8%A4%87%E8%A3%BD%E8%B2%BC%E4%B8%8A-1554d5a81da0),超級有用 參考上述文章去下載 virtualbox-guest-x11 會發現有很多尚未更新的。 ![image](https://hackmd.io/_uploads/SJcTkdd_kx.png) 這時候就無法成功完成雙向設定 ![image](https://hackmd.io/_uploads/HyI2kdu_Jl.png) 更新 apt ``` sudo apt upgrade ``` 再一次下載 virtualbox-guest-x11,原本沒更新的更新完成了 ![image](https://hackmd.io/_uploads/rJWYJd_O1x.png) 重新開機 ![image](https://hackmd.io/_uploads/ryh38uu_kg.png) ![image](https://hackmd.io/_uploads/HJhusdOdJe.png) ![image](https://hackmd.io/_uploads/Bk3Usuudkx.png) 可以雙向複製貼上了 ![image](https://hackmd.io/_uploads/HyL16_Odye.png) ### 安裝 Prerequisites 參考[官方文件](https://hyperledger-fabric.readthedocs.io/en/latest/prereqs.html),裡面提到在正式使用 HLF 之前要先安裝 git, cURL, Docker。 ```sh= sudo apt-get install git curl docker-compose -y # Make sure the Docker daemon is running. sudo systemctl start docker # Add your user to the Docker group. sudo usermod -a -G docker <username> # Check version numbers docker --version docker-compose --version # Optional: If you want the Docker daemon to start when the system starts, use the following: sudo systemctl enable docker ``` 因為之後可能會需要用到智能合約,所以還要安裝 Go。按照[官方文件](https://golang.org/doc/install)指示刪掉之前版本,安裝最新的。 ![image](https://hackmd.io/_uploads/HJ4nniwu1x.png) ``` rm -rf /usr/local/go && tar -C /usr/local -xzf go1.23.5.linux-amd64.tar.gz ``` ``` export PATH=$PATH:/usr/local/go/bin ``` ![image](https://hackmd.io/_uploads/rkoA2hDOyg.png) 最後是安裝最新版本的 jq,[官方網站](https://jqlang.github.io/jq/download/)都有給予對應作業系統要用的安裝指令 > - go: 1.23.5 > - Docker version 26.1.3 ## 安裝 Fabric 範本 按照[官方文件](https://hyperledger-fabric.readthedocs.io/en/latest/install.html) ``` mkdir -p $HOME/go/src/github.com/<your_github_userid> cd $HOME/go/src/github.com/<your_github_userid> ``` ``` curl -sSLO https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh && chmod +x install-fabric.sh ``` 使用 docker 去下載 Fabric Container Images,接著clone fabric-samples github repo 並下載 Fabric binaries ``` ./install-fabric.sh docker samples binary ``` 選擇特定版本 ``` ./install-fabric.sh --fabric-version 2.5.10 binary ``` 總之安裝完成大概會看到這些 ``` root@Ubuntu2204LTS:~/go/src/github.com/<github-user-name># ls bin builders config fabric-samples install-fabric.sh ``` ## [建立測試環境的網路](https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html) ``` cd fabric-samples/test-network ./network.sh up ``` ![image](https://hackmd.io/_uploads/SJvSPJOu1g.png) 可以看到三個 container 建立好了。這個 Fabric 網路包含兩個 peer 和一個 ordering node ![image](https://hackmd.io/_uploads/rJgdwJd_ke.png) ## 建立 channel 通道(Channel)是其架構中一個至關重要的概念,主要用於實現不同參與者之間的私密交易和數據隔離。以下是通道在Hyperledger Fabric中的重要性詳細說明。 通道的基本概念 定義:通道是一個邏輯結構,允許特定組織之間進行私有交易。每個通道都有自己的賬本(ledger),並且可以運行多個鏈碼(chaincode),這使得不同的業務應用能夠在同一網絡中獨立運行而不互相干擾 我們用[官方文件](https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html)的範例在兩個機構之間建立 channel,這個新的 channel 會叫 "mychannel",這是因為 `network.config` 這個檔案預先設定的。 ![image](https://hackmd.io/_uploads/SkDZgF_uyg.png) ``` ./network.sh createChannel ``` ![image](https://hackmd.io/_uploads/ByoYxKddke.png) 自定義名稱可以用 ``` ./network.sh createChannel -c <customized_name> ``` :::spoiler 解決 Error: proposal failed (err: bad proposal response 500: cannot create ledger from genesis block: ledger [mychannel] already exists with state [ACTIVE]) After 5 attempts, peer0.1 has failed to join channel 'mychannel' ### 錯誤 ![image](https://hackmd.io/_uploads/SkFy60MFyl.png) ``` Error: proposal failed (err: bad proposal response 500: cannot create ledger from genesis block: ledger [mychannel] already exists with state [ACTIVE]) After 5 attempts, peer0.org1 has failed to join channel 'mychannel' ``` ### 解決 ``` ./network.sh down docker system prune --volumes ``` ![image](https://hackmd.io/_uploads/H1zIp0MY1x.png) ![image](https://hackmd.io/_uploads/HkzSpAfKkg.png) https://github.com/hyperledger/fabric-samples/issues/1097 ::: ## 把 peer 加入到環境變數 按照[官方文件](https://hyperledger-fabric.readthedocs.io/en/release-2.4/test_network.html),進入到 `test-network` 路徑,輸入以下指令。如果前面的 pre-required 都有安裝正確的話,就可以成功抓到位於 fabric-samples/bin 的 peer binaries 檔案。 ``` export PATH=${PWD}/../bin:$PATH export FABRIC_CFG_PATH=$PWD/../config/ ``` 輸入 `peer`,成功 ![image](https://hackmd.io/_uploads/rkq35qud1x.png) 這樣就可以執行許多指令,例如 - 查看目前有哪些 channel: `peer channel list` ![image](https://hackmd.io/_uploads/rJy9BlXKkx.png) - 查詢某通道的區塊編號(height) `peer channel getinfo -c <channel_name>` ![image](https://hackmd.io/_uploads/HyvRHgQYye.png) - 取得通道的創世區塊(Genesis Block)`peer channel fetch config genesis_block.pb -c <通道名稱> --orderer <訂購服務地址>` > The first block of a newly created channel is known as a “genesis block” :::spoiler 如何查看網路配置的組織、通道、訂購服務(Orderer)、策略 `configtx.yaml`是網路設定檔,通常放在 Fabric 網路的配置資料夾。它定義了 組織、通道、訂購服務(Orderer)、策略 。 ``` cat configtx/configtx.yaml ``` ::: :::spoiler 如何查看一個通道上有哪些機構 抓取通道組態區塊 ``` peer channel fetch config config_block.pb -o localhost:7050 \ --ordererTLSHostnameOverride orderer.example.com --tls \ --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" \ -c mychannel ``` ![image](https://hackmd.io/_uploads/B1oO9lXFJx.png) 轉換區塊為 JSON 格式,儲存為 config.json ``` configtxlator proto_decode --input config_block.pb --type common.Block \ | jq .data.data[0].payload.data.config > config.json ``` 查看有哪些機構 ``` jq -r '.channel_group.groups.Application.groups | keys' config.json ``` ![image](https://hackmd.io/_uploads/Byvd7nOuJl.png) ::: ### Crypto Generator ## [在 channel 建立智慧合約](https://hyperledger-fabric.readthedocs.io/en/latest/deploy_chaincode.html) 區塊鏈的使用者是透過執行智慧合約來跟帳本互動的。在 Hyperledger Fabric 裡,這些智慧合約是打包成 chaincode 來部署的,也就是說我們常聽到的 smart contract 是一個邏輯層面的概念,描述的是區塊鏈中資產和交易的規則和邏輯。Chaincode 是智慧合約在 Fabric 中的具體表現形式,包含了智慧合約的業務邏輯,並運行於區塊鏈節點之上。 如果某個組織想要驗證交易或查詢帳本,就需要在自己的對等節點 (peers) 上安裝 chaincode。當 chaincode 安裝到已加入某個通道的節點後,通道裡的成員就可以正式部署它,然後用裡面的智慧合約來新增或更新帳本上的資產。 ### peer lifecycle chaincode 部署 chaincode 的過程叫做 Fabric chaincode lifecycle ,以確保所有相關的組織都能在 chaincode 正式運行前,先達成共識。舉個例子,endorsement policy 會定義哪些組織需要執行 chaincode 來驗證交易,而通道成員必須透過這個生命週期流程來決定最終的 endorsement policy。 ### 建立 chaincode 部署 chaincode 其時有四個步驟 1. Step one: Package the smart contract 2. Step two: Install the chaincode package 3. Step three: Approve a chaincode definition 4. Step four: Committing the chaincode definition to the channel 官方文件說使用 ``` ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go ``` :::spoiler 記得幫 go 設置環境變數 如果遇到以下 error ![image](https://hackmd.io/_uploads/r18RBtdO1g.png) 雖然之前安裝 Prerequisites 的時候有做過,但這邊要再一次設置環境變數。這是因為 go 官方文檔提供的環境變數變更只會影響當前的 Shell Session,如果開啟一個新的終端或切換到另一個目錄,原本的 export PATH 設定就不會自動保留,因為環境變數在不同的 Shell Session 之間不會自動傳遞。官方提供的環境變數設置如下: ``` export PATH=$PATH:/usr/local/go/bin ``` 如果不想每次切換目錄或開啟新終端時都要重新設置 PATH,可以將它添加到 Shell 配置檔案,對於 Ubuntu 就是修改 Bash ``` echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc source ~/.bashrc ``` ::: 再執行建立 chaincode 的指令,就可以看到建立成功的結果: ![image](https://hackmd.io/_uploads/HJ2Fxi_dye.png) ### 應用 chaincode 範例 - 轉移資產 透過設置環境變數來指定查詢的對象 ``` export PATH=${PWD}/../bin:$PATH export FABRIC_CFG_PATH=$PWD/../config/ export CORE_PEER_TLS_ENABLED=true export CORE_PEER_LOCALMSPID=Org1MSP export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp export CORE_PEER_ADDRESS=localhost:7051 ``` :::spoiler 否則等等執行指令會出現以下問題 ![image](https://hackmd.io/_uploads/Skv3Xidd1g.png) ![image](https://hackmd.io/_uploads/r1Gi9u9uJl.png) ``` 2025-01-30 14:50:16.554 CST 0001 ERRO [main] InitCmd -> Cannot run peer because cannot init crypto, specified path "/root/go/src/github.com/<user-name>/fabric-samples/config/msp" does not exist or cannot be accessed: stat /root/go/src/github.com/<user-name>/fabric-samples/config/msp: no such file or directory ``` https://stackoverflow.com/questions/66651603/cannot-run-peer-because-cannot-init-crypto-error-while-using-peer-command ::: 環境變數設定完成後執行 ``` peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" -c '{"function":"InitLedger","Args":[]}' ``` ![image](https://hackmd.io/_uploads/H1VmEodOJx.png) 當區塊鏈網路的成員想要轉移或修改帳本上的資產時,就會執行 chaincode。可以用下面的指令來調用「資產轉移 (基本版)」chaincode,來變更帳本上某個資產的擁有者 變更前輸入以下指令就可以看目前的資產狀態 ``` peer chaincode query -C mychannel -n basic -c '{"Args":["GetAllAssets"]}' ``` ```json [ {"AppraisedValue":300,"Color":"blue","ID":"asset1","Owner":"Tomoko","Size":5}, {"AppraisedValue":400,"Color":"red","ID":"asset2","Owner":"Brad","Size":5}, {"AppraisedValue":500,"Color":"green","ID":"asset3","Owner":"Jin Soo","Size":10}, {"AppraisedValue":600,"Color":"yellow","ID":"asset4","Owner":"Max","Size":10}, {"AppraisedValue":700,"Color":"black","ID":"asset5","Owner":"Adriana","Size":15}, {"AppraisedValue":800,"Color":"white","ID":"asset6","Owner":"Michel","Size":15} ] ``` 透過 chaincode 變更。這條指令的意思是"在 mychannel 上調用 basic chaincode,執行 TransferAsset 函數,把 ID 為 asset6 的資產轉移給 Christopher,並透過 Orderer 廣播交易,讓 Org1 和 Org2 的 peer0 節點進行驗證。" ``` peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" -c '{"function":"TransferAsset","Args":["asset6","Christopher"]}' ``` ![image](https://hackmd.io/_uploads/r1GjUsOdye.png) 再次用指令驗證目前資產轉移結果,成功 ```json [ {"AppraisedValue":300,"Color":"blue","ID":"asset1","Owner":"Tomoko","Size":5}, {"AppraisedValue":400,"Color":"red","ID":"asset2","Owner":"Brad","Size":5}, {"AppraisedValue":500,"Color":"green","ID":"asset3","Owner":"Jin Soo","Size":10}, {"AppraisedValue":600,"Color":"yellow","ID":"asset4","Owner":"Max","Size":10}, {"AppraisedValue":700,"Color":"black","ID":"asset5","Owner":"Adriana","Size":15}, {"AppraisedValue":800,"Color":"white","ID":"asset6","Owner":"Christopher","Size":15} ] ``` ### [建立自己的智能合約](https://hyperledger-fabric.readthedocs.io/en/latest/deploy_chaincode.html) ## MSP 雖然名字叫「Membership Service Provider(MSP,成員服務提供者)」,但它其實並不會「提供」任何服務。MSP 的實作方式其實就是一組資料夾,這些資料夾被加入到網路的設定中,並用來定義一個組織的身份: - **內部管理**:組織可以透過 MSP 來決定誰是管理員。 - **外部驗證**:其他組織可以透過 MSP 來確認某個身份是否有權限執行特定操作。 與此同時,**憑證授權機構(CA,Certificate Authority)** 負責發放代表身份的憑證,而 **MSP** 則是維護一個授權身份的清單,也就是說,MSP 紀錄了哪些身份被允許參與網路。 ### MSP 的作用 MSP 會透過以下方式來決定哪些 Root CA(根憑證機構)和 Intermediate CA(中介憑證機構)是可信的: 1. **列出成員身份**:直接記錄哪些身份被允許加入。 2. **授權 CA 發放身份**:指定哪些 CA 可以發放有效的身份憑證。 但 MSP 的功能不僅限於定義誰能參與網路或某個頻道(Channel),它還負責**將身份與角色綁定**,進而決定該身份在網路中的權限。例如,當使用 Fabric CA 註冊一個身份時,必須賦予該身份一個角色,如: - **Admin(管理員)** - **Peer(節點)** - **Client(客戶端)** - **Orderer(排序節點)** - **Member(成員)** 舉例來說: - 註冊為「Peer」角色的身份,應該被分配給 Peer 節點。 - 註冊為「Admin」角色的身份,應該交給組織的管理者。 稍後我們會更深入探討這些角色的具體作用。 ### MSP 還能做什麼? 除了定義合法身份,MSP 還可以用來管理**已撤銷的身份**。如果某個身份被撤銷,它會被加入 MSP 的黑名單,這樣網路中的其他節點就能知道這個身份已經失效。我們稍後會進一步介紹這個過程的運作方式。 --- ## 機構底下有什麼? ![image](https://hackmd.io/_uploads/rkYrAAztJl.png) ### CA Certificate ![image](https://hackmd.io/_uploads/rJFFAAMtye.png) ### peers ![image](https://hackmd.io/_uploads/SkanA0Mt1g.png) ### MSP ![image](https://hackmd.io/_uploads/B1RkkJQtye.png) --- ## CA 在憑證授權機構(CA)負責處理組織內所有實體的身份,所謂實體包含了網路元件和網路使用者。每個實體都會從CA獲得一個憑證(crypto material,加密材料)。該憑證包含一個私鑰以及代表該實體的數位證書。只要實體保持私鑰的安全,安全性就能得到維護。 Hyperledger Fabric 提供了 cryptogen 這個套件,用以建立 crypto material, ## crypto material 檔案路徑結構 ![image](https://hackmd.io/_uploads/BJDtWdXYkg.png) ## CA Server 建立 crypto material 前面提到的`./network.sh up`指令只會建立一個基本的 Fabric 網路,包含兩個 Peer 節點和一個 Orderer 節點。預設情況下,它使用 cryptogen 工具來產生組織的憑證和金鑰,並透過 docker-compose-test-net.yaml 啟動 Peer 和 Orderer 節點。 而`./network.sh up -ca`則進一步加入「憑證授權機構(Certificate Authorities,CA)」來產生加密材料,而不是使用 cryptogen。它會使用 Fabric CA 伺服器的設定檔,並透過 organizations/fabric-ca 目錄下的 registerEnroll.sh 腳本來處理註冊和認證。Fabric CA 會在 organizations 目錄內為所有組織建立加密材料和 MSP(Membership Service Provider)資料夾。 ``` ./network.sh up -ca ``` ![image](https://hackmd.io/_uploads/BJZyEk7FJl.png) ![image](https://hackmd.io/_uploads/Sko5XJ7tJg.png) ![image](https://hackmd.io/_uploads/r1jIXkXtyx.png) ![image](https://hackmd.io/_uploads/Sk3GEymF1l.png) 使用 HLF 的 cryptogen 工具建立加密材料,和使用 CA Server 建立加密材料,其中一個差別在於 private secret key 儲存的地方,使用 CA Server 建立的話 PSK 就會位於遠端的伺服器。 ![image](https://hackmd.io/_uploads/ByDpYyXKye.png) ![image](https://hackmd.io/_uploads/S1hvpkQKye.png) ## [自己寫一個 chaincode](https://hyperledger-fabric.readthedocs.io/en/latest/chaincode4ade.html) 在 Hyperledger Fabric 中,Chaincode(鏈碼)通常負責處理網路成員共同認可的業務邏輯,因此它的概念類似於「智能合約」。當我們想要更新或查詢帳本時,可以透過交易請求(proposal transaction)來調用 chaincode。 ### Fabric 鏈碼生命週期(Chaincode Lifecycle) Fabric Chaincode Lifecycle 是一個流程,允許多個組織**在鏈碼上線前**先達成共識,確保大家都同意它的運作方式,才能讓它在通道上使用。 在這個流程中,**網路運營者(Network Operator)** 會使用 **Fabric 鏡碼生命週期** 來執行以下任務: 1. **安裝並定義鏈碼**(Install and define a chaincode)— 先將鏈碼安裝到節點,然後定義它的參數,例如名稱、版本、策略等。 2. **升級鏈碼**(Upgrade a chaincode)— 當需要對鏈碼進行修改時,可以透過生命週期流程升級它,確保新版本獲得組織的同意後再部署。 3. **不同的部署場景**(Deployment Scenarios)— 針對不同的業務需求,Fabric 提供多種鏈碼部署方式,例如單組織或多組織協作模式。 4. **遷移到新版生命週期**(Migrate to the new Fabric lifecycle)— 如果你的網路還在使用舊版鏈碼管理方式,建議遷移到新版 **Fabric 鏈碼生命週期**,以獲得更靈活的治理能力。 這個流程確保了**區塊鏈網路的安全性與一致性**,避免單一組織隨意更改鏈碼影響其他成員 ### 安裝並定義鏈碼(Install and define a chaincode) 在 Fabric 鏈碼生命週期 中,各組織需要事先達成共識,確定鏈碼的參數,例如:名稱(Name)、版本(Version)、背書策略(Endorsement Policy) 通道中的成員組織需要透過以下四個步驟來完成鏈碼定義,不是每個組織都需要執行所有步驟: #### 1. 打包鏈碼 - 這個步驟可以由某個組織單獨執行,也可以讓每個組織都自行打包。 ``` peer lifecycle chaincode package mychaincode.tar.gz --path ./chaincode/ --lang golang --label mychaincode_1 ``` ![image](https://hackmd.io/_uploads/SytRQ-Xtyl.png) 鏈碼必須打包為 `.tar.gz` 格式的 tar 文件,並且該 tar 文件必須包含兩個文件(無目錄結構): - metadata.json:元數據文件,包含鏈碼語言、代碼路徑和包標籤。 ```json // metadata.json { "Path": "fabric-samples/asset-transfer-basic/chaincode-go", "Type": "golang", "Label": "basicv1" } ``` - code.tar.gz:包含鏈碼文件的壓縮包。 #### 2. 安裝鏈碼到 Peer 節點 - 每個組織 如果要使用這個鏈碼來背書交易或查詢帳本,都需要完成這一步。 - 以下指令會將 mychaincode.tar.gz 安裝到本節點(Peer)。 ``` peer lifecycle chaincode install mychaincode.tar.gz ``` 鏈碼包需要安裝到每個執行和背書交易的對等節點上。無論使用 CLI 還是 SDK,都需要以**對等節點管理員(Peer Administrator)**身份執行這一步。 當鏈碼安裝到對等節點後,對等節點會嘗試構建鏈碼。如果鏈碼有問題,則會返回構建錯誤。因此,建議每個組織僅打包一次鏈碼,然後將相同的包安裝到組織內的所有對等節點上。 如果希望確保通道(channel)內的所有組織運行相同的鏈碼,則可以由一個組織打包鏈碼,並**通過線下方式(out of band)**將其發送給其他成員。 成功執行安裝命令後,將返回鏈碼包標識符(package identifier),該標識符是鏈碼標籤 + 鏈碼包哈希值的組合。此標識符將用於將安裝在對等節點上的鏈碼與組織批准的鏈碼定義(Chaincode Definition)關聯起來,請保存此標識符以供下一步使用。 也可以使用 Peer CLI 查詢已安裝的鏈碼來獲取 package identifier。 ![image](https://hackmd.io/_uploads/B1BMH-XKJg.png) 鏈碼安裝流程 Org1 和 Org2 的對等節點管理員將 MYCC_1 鏈碼安裝到加入通道的對等節點上,並生成鏈碼包標識符 MYCC_1:hash。 #### 3. 核准鏈碼定義 - 每個希望使用該鏈碼的組織都需要批准它的鏈碼定義。 - 只有當足夠的組織批准(通常是多數同意)後,鏈碼才能啟動 - 以下指令讓組織批准 mychaincode,並指定 sequence(鏈碼版本遞增)與 package-id(鏈碼包 ID)。 ``` peer lifecycle chaincode approveformyorg -o orderer.example.com:7050 --channelID mychannel --name mychaincode --version 1.0 --package-id mychaincode_1:hash --sequence 1 --init-required ``` 鏈碼受鏈碼定義(Chaincode Definition)管理。當通道成員批准鏈碼定義時,該批准相當於該組織對鏈碼參數的投票。批准後的鏈碼定義允許通道成員在鏈碼運行前達成一致。 鏈碼定義包括以下必須在所有組織之間保持一致的參數: 1. 名稱(Name):應用程式在調用鏈碼時使用的名稱。 2. 版本(Version):鏈碼版本號。如果升級鏈碼,則需要更新版本號。 3. 序列號(Sequence):鏈碼定義的更新次數,每次升級鏈碼時,序列號遞增。例如,首次安裝時 Sequence = 1,升級後變為 Sequence = 2。 4. 背書策略(Endorsement Policy):哪些組織需要執行並驗證交易輸出。默認為 Channel/Application/Endorsement,即通道內的多數組織需進行背書。 5. 私有數據集合配置(Collection Configuration):鏈碼使用的私有數據集合文件路徑(如適用)。 6. ESCC/VSCC 插件:自定義背書或驗證插件名稱(如適用)。 7. 初始化函數(Initialization):如果使用 Fabric Chaincode Shim API 的底層 API,鏈碼必須包含 Init 方法來初始化狀態。可以通過 --init-required 標誌強制要求初始化。 #### 4. 提交鏈碼定義 - 當足夠多的組織批准後,任意一個組織 可以負責提交(commit)這個鏈碼定義,讓它正式運行。 - 這步驟會收集足夠的背書,然後提交交易,最終鏈碼才能在通道上運行。 ``` peer lifecycle chaincode commit -o orderer.example.com:7050 --channelID mychannel --name mychaincode --version 1.0 --sequence 1 --init-required ``` ![image](https://hackmd.io/_uploads/H1aCEWmFyg.png) ### 撰寫 chaincode 注意事項 https://www.youtube.com/watch?v=80w8yiud4Vc ## 組織內配置不同角色 ## 新組織加入 ## [自己寫一個 chaincode](https://hyperledger-fabric.readthedocs.io/en/latest/chaincode4ade.html) ``` mkdir myFirstChainCode && cd myFirstChainCode ``` ```bash go mod init myFirstChainCode # create go.mod file touch myFirstChainCode.go ``` ![image](https://hackmd.io/_uploads/BJShzr25Jg.png) ![image](https://hackmd.io/_uploads/H1UqQrn5ye.png) ``` nano myFirstChainCode.go ``` ::: spoiler chaincode ```go // 以 Asset Transfer Chaincode 為例子 package main import ( "encoding/json" "fmt" "log" "github.com/hyperledger/fabric-contract-api-go/contractapi" ) // SmartContract defines the federated learning contract type SmartContract struct { contractapi.Contract } // Model represents a federated learning model type Model struct { ModelID string `json:"modelID"` Owner string `json:"owner"` ModelHash string `json:"modelHash"` Aggregated bool `json:"aggregated"` Contributors []string `json:"contributors"` } // InitLedger initializes the ledger with a base model func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error { models := []Model{ {ModelID: "model1", Owner: "admin", ModelHash: "abc123", Aggregated: false, Contributors: []string{}}, } for _, model := range models { modelJSON, err := json.Marshal(model) if err != nil { return err } err = ctx.GetStub().PutState(model.ModelID, modelJSON) if err != nil { return fmt.Errorf("failed to put model in ledger: %v", err) } } return nil } // SubmitModelUpdate allows nodes to submit local model updates func (s *SmartContract) SubmitModelUpdate(ctx contractapi.TransactionContextInterface, modelID string, modelHash string) error { modelJSON, err := ctx.GetStub().GetState(modelID) if err != nil { return fmt.Errorf("failed to get model: %v", err) } if modelJSON == nil { return fmt.Errorf("model %s does not exist", modelID) } var model Model err = json.Unmarshal(modelJSON, &model) if err != nil { return err } // Get submitter identity clientID, err := ctx.GetClientIdentity().GetID() if err != nil { return fmt.Errorf("failed to get client identity: %v", err) } // Zero Trust: Verify client authorization if !s.isAuthorized(clientID) { return fmt.Errorf("access denied: unauthorized client") } // Store new update model.ModelHash = modelHash model.Contributors = append(model.Contributors, clientID) updatedModelJSON, err := json.Marshal(model) if err != nil { return err } return ctx.GetStub().PutState(modelID, updatedModelJSON) } // AggregateModel finalizes the model after validation func (s *SmartContract) AggregateModel(ctx contractapi.TransactionContextInterface, modelID string) error { modelJSON, err := ctx.GetStub().GetState(modelID) if err != nil { return fmt.Errorf("failed to get model: %v", err) } if modelJSON == nil { return fmt.Errorf("model %s does not exist", modelID) } var model Model err = json.Unmarshal(modelJSON, &model) if err != nil { return err } if len(model.Contributors) < 2 { // Example consensus: at least 2 updates return fmt.Errorf("not enough contributors for aggregation") } model.Aggregated = true updatedModelJSON, err := json.Marshal(model) if err != nil { return err } return ctx.GetStub().PutState(modelID, updatedModelJSON) } // isAuthorized enforces zero-trust access control (simplified) func (s *SmartContract) isAuthorized(clientID string) bool { allowedClients := []string{"Org1MSP", "Org2MSP"} // Replace with actual verification for _, id := range allowedClients { if clientID == id { return true } } return false } // ReadModel retrieves the model details func (s *SmartContract) ReadModel(ctx contractapi.TransactionContextInterface, modelID string) (*Model, error) { modelJSON, err := ctx.GetStub().GetState(modelID) if err != nil { return nil, fmt.Errorf("failed to read model: %v", err) } if modelJSON == nil { return nil, fmt.Errorf("model %s does not exist", modelID) } var model Model err = json.Unmarshal(modelJSON, &model) if err != nil { return nil, err } return &model, nil } // GetAllModels retrieves all models func (s *SmartContract) GetAllModels(ctx contractapi.TransactionContextInterface) ([]*Model, error) { resultsIterator, err := ctx.GetStub().GetStateByRange("", "") if err != nil { return nil, err } defer resultsIterator.Close() var models []*Model for resultsIterator.HasNext() { queryResponse, err := resultsIterator.Next() if err != nil { return nil, err } var model Model err = json.Unmarshal(queryResponse.Value, &model) if err != nil { return nil, err } models = append(models, &model) } return models, nil } func main() { modelChaincode, err := contractapi.NewChaincode(&SmartContract{}) if err != nil { log.Panicf("Error creating federated learning chaincode: %v", err) } if err := modelChaincode.Start(); err != nil { log.Panicf("Error starting federated learning chaincode: %v", err) } } ``` ::: ### 打包 chaincode 由於這些 code 不在 libraries 裡面,所以要先打包,之後才可以裝到 peers 節點裡面。首先我們要先把檔案變成壓縮檔: 1. 轉換成壓縮檔案 `.tar.gz` ``` tar -cvf <myproject>.tar . gzip myproject.tar ``` 2. - peer chaincode package - 用于将链码打包成一个 .cds 文件(链码部署规范文件),以便在网络中的对等节点上安装和实例化链码。 - 使用时机:在准备将链码部署到 Fabric 网络时,需要先将链码打包成 .cds 文件。 ```bash peer chaincode package ``` - peer chaincode install - 用于在对等节点上安装打包好的链码。安装后,链码会被存储在对等节点的文件系统中,但还未在通道上实例化。 - 使用时机:在对等节点上安装链码后,需要在通道上实例化链码才能使其可用。 ```bash peer chaincode install ``` ### 管理與外部套件的依賴關係 以下兩行指令的詳細說明可以參考文章 [【go语言】Go基础知识:go.mod文件、go mod命令、私有仓库、导入版本管理、Vendor目录详解](https://www.cnblogs.com/shangxiaofei/articles/18568463)。 ```bash go mod tidy go mod vendor # This places the external dependencies for your chaincode into a local vendor directory. ``` ![image](https://hackmd.io/_uploads/HywitSh9Je.png) (中間還有一堆) ![image](https://hackmd.io/_uploads/ryQH9r3qJx.png) Once dependencies are vendored in your chaincode directory, peer chaincode package and peer chaincode install operations will then include code associated with the dependencies into the chaincode package. ### chaincode 權限控制 透過`ctx.GetStub().GetCreator()` API 取得用戶的 certificate ## [peer](https://hyperledger-fabric.readthedocs.io/en/latest/peers/peers.html) (non-ordering nodes) >[!Note] >簡單來說,Peer 是帳本、鏈碼和服務的託管者,因此客戶端應用 (client applications) 和管理者 (administrators) 都必須透過 Peer 來訪問這些資源。 - Peers 是執行鏈碼 (Chaincode) 並維護帳本 (Ledger) 的核心節點,不同於其他區塊鏈的礦工節點。 - 管理 ledgers 和 smart contracts - 帳本 (Ledger) - 是不可變的 (Immutable): Fabric 的 帳本 會記錄所有交易,而且一旦寫入,就無法被修改或刪除,這是區塊鏈的核心特性之一。 - 智慧合約 (Smart Contracts) - 負責產生交易 - 在 Fabric 中,智慧合約 (Smart Contract) 是定義商業邏輯的程式碼,它會處理資產轉移、權限管理等邏輯。 - 負責定義 「如何處理交易」(即「流程」)。 - 鏈碼 (Chaincode) 是智慧合約的實作方式,類似於其他區塊鏈平台(如 Ethereum)中的智能合約。 > [!Note] > Peer 可以同時託管多個帳本,這樣的設計讓 Fabric 網路更具靈活性,因為一個 Peer 可以同時參與多個通道 (channels)。鏈碼 (chaincode) 是部署在特定通道上的,而每個通道(和帳本)可以有多個鏈碼與之互動。 > ![image](https://hackmd.io/_uploads/rk-_IU2c1g.png) > [!Note] > 因為區塊鏈需要在 同一通道 (channel) 內的 Peer 之間維持一致的數據副本 (consistent replicas of data) 和智慧合約 (smart contracts)。這種設計在 Fabric 網路中提供了刻意的冗餘 (deliberate redundancy),藉此避免單點故障 (single points of failure),確保帳本 (ledger) 的一致性與即時性。 - 透過 Fabric Gateway service 管理 transaction proposals 和 endorsements - 交易需要獲得必要組織的背書 (Endorsement) - Fabric 採用「授權機制 (Endorsement Policy)」,不同於公鏈的「共識機制 (Proof of Work/Stake)」。 - 只有獲得指定組織的認可 (Endorsed by required organizations),交易才會被視為有效,這樣能夠實現隱私控制,讓不同組織之間的交易保持透明但又不公開給所有人。 ![image](https://hackmd.io/_uploads/H1jl1I2c1x.png) 每個節點都有一份 copy 的 ledger,然後都 invoke 同一份 chaincode ## Fabric Gateway 交易的處理分為三個階段: 1. 交易提案與背書(提交交易請求 → 執行鏈碼 → 收集背書)。 2. 交易提交與排序(提交至 Orderer → 排序並打包區塊)。 3. 交易驗證與提交(檢查交易有效性 → 更新帳本 → 通知應用程式)。 以上三階段 Gateway SDK 已經自動處理,開發者只需使用 SDK 即可完成。 ## Peers 和 channel ![image](https://hackmd.io/_uploads/SJguei3ckl.png) ## Peers 和 Organizations ![image](https://hackmd.io/_uploads/BkAtesh9Jx.png) --- ## 參考 - [](https://kctheservant.medium.com/two-ways-to-generate-crypto-materials-in-hyperledger-fabric-cryptogen-and-ca-server-36d3c3e2daad) - [link text](https:// "title")