---
# System prepended metadata

title: Hyperledger Fabric
tags: [專題]

---

# 部署 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")

