前言
作者:Rightstar 微信:Rightstar_ CSDN:https://blog.csdn.net/qq_33886316 掘金:https://juejin.cn/user/2375425082269832
最近使用fabric 的Go SDK搭建了个Go服务器,完整项目还包括链代码的调用以及相关连接配置文件,这里只给出区块链浏览器相关的构建代码。由于百度博客找的资料太少或者又是不全,分享下希望对大家有帮助。
效果图(Vue前端)
部分数据简单地模拟了下 主要功能包括可以查询最新的区块以及通过交易ID查询交易详细信息  
参考博客
https://blog.csdn.net/AlexTan_/article/details/110826476 区块体结构:
fabric连接服务
本人使用了iris搭建了Go Rest API这里只给出具体的区块及交易载荷的解析(参考博客熟悉区块体结构) 相关文件结构: 
关键代码
entity.go
package services
type Block struct {
Number uint64 `json:"number"`
PreviousHash []byte `json:"previousHash"`
DataHash []byte `json:"dataHash"`
BlockHash []byte `json:"blockHash"`
TxNum int `json:"txNum"`
TransactionList []*Transaction `json:"transactionList"`
CreateTime string `json:"createTime"`
}
type Transaction struct {
TransactionActionList []*TransactionAction `json:"transactionActionList"`
}
type TransactionAction struct {
TxId string `json:"txId"`
BlockNum uint64 `json:"blockNum"`
Type string `json:"type"`
Timestamp string `json:"timestamp"`
ChannelId string `json:"channelId"`
Endorsements []string `json:"endorsements"`
ChaincodeId string `json:"chaincodeId"`
ReadSetList []string `json:"readSetList"`
WriteSetList []string `json:"writeSetList"`
}
chainBrowserService.go
package services
import (
"encoding/json"
"fmt"
"github.com/hyperledger/fabric-sdk-go/pkg/client/ledger"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
"github.com/hyperledger/fabric-sdk-go/pkg/util/pathvar"
"log"
)
var mainSDK *fabsdk.FabricSDK
var ledgerClient *ledger.Client
const (
org1Name = "Org1"
org2Name = "Org2"
org1Peer0 = "peer0.org1.example.com"
org1AdminUser = "Admin"
org2AdminUser = "Admin"
org1User = "User1"
org2User = "User1"
channelID = "mychannel"
windowConfigPath = "D:/GoProject/src/github.com/rightstar/fabric-client-config/config/config.yaml"
linuxConfigPath = "/root/go/src/github.com/rightstar/fabric-client-config/config/linuxConfig.yaml"
)
var chainBrowserConfigPath = windowConfigPath
func InitChainBrowserService(){
log.Println("============ 初始化区块浏览器服务 ============")
var err error
ConfigBackend := config.FromFile(pathvar.Subst(chainBrowserConfigPath))
mainSDK, err = fabsdk.New(ConfigBackend)
if err != nil {
panic(fmt.Sprintf("Failed to create new SDK: %s", err))
}
org1AdminChannelContext := mainSDK.ChannelContext(channelID, fabsdk.WithUser(org1AdminUser), fabsdk.WithOrg(org1Name))
ledgerClient, err = ledger.New(org1AdminChannelContext)
if err != nil {
fmt.Printf("Failed to create new resource management client: %s", err)
}
}
func QueryLedgerInfo() (*fab.BlockchainInfoResponse,error){
ledgerInfo, err := ledgerClient.QueryInfo()
if err != nil {
fmt.Printf("QueryInfo return error: %s", err)
return nil, err
}
return ledgerInfo,nil
}
func QueryLatestBlocksInfo() ([]*Block,error){
ledgerInfo, err := ledgerClient.QueryInfo()
if err != nil {
fmt.Printf("QueryLatestBlocksInfo return error: %s\n", err)
return nil, err
}
latestBlockList :=[]*Block{}
lastetBlockNum :=ledgerInfo.BCI.Height-1
for i:=lastetBlockNum;i>0&&i>(lastetBlockNum-5);i--{
block,err :=QueryBlockByBlockNumber(int64(i))
if err != nil {
fmt.Printf("QueryLatestBlocksInfo return error: %s", err)
return latestBlockList, err
}
latestBlockList = append(latestBlockList,block)
}
return latestBlockList,nil
}
func QueryLatestBlocksInfoJsonStr()(string,error){
blockList,err:=QueryLatestBlocksInfo()
jsonStr,err :=json.Marshal(blockList)
return string(jsonStr),err
}
func QueryBlockByBlockNumber(num int64) (*Block,error){
rawBlock,err :=ledgerClient.QueryBlock(uint64(num))
if err != nil {
fmt.Printf("QueryBlock return error: %s", err)
return nil, err
}
txList :=[]*Transaction{}
for i :=range rawBlock.Data.Data{
rawEnvelope,err :=GetEnvelopeFromBlock(rawBlock.Data.Data[i])
if err != nil {
fmt.Printf("QueryBlock return error: %s", err)
return nil, err
}
transaction, err :=GetTransactionFromEnvelopeDeep(rawEnvelope)
if err != nil {
fmt.Printf("QueryBlock return error: %s", err)
return nil, err
}
for i :=range transaction.TransactionActionList {
transaction.TransactionActionList[i].BlockNum=rawBlock.Header.Number
}
txList= append(txList,transaction)
}
block :=Block{
Number: rawBlock.Header.Number,
PreviousHash: rawBlock.Header.PreviousHash,
DataHash: rawBlock.Header.DataHash,
BlockHash: rawBlock.Header.DataHash,
TxNum: len(rawBlock.Data.Data),
TransactionList: txList,
CreateTime: txList[0].TransactionActionList[0].Timestamp,
}
return &block,nil
}
func QueryTransactionByTxId(txId string) (*Transaction,error){
rawTx,err :=ledgerClient.QueryTransaction(fab.TransactionID(txId))
if err != nil {
fmt.Printf("QueryBlock return error: %s", err)
return nil, err
}
transaction, err :=GetTransactionFromEnvelopeDeep(rawTx.TransactionEnvelope)
if err != nil {
fmt.Printf("QueryBlock return error: %s", err)
return nil, err
}
block,err :=ledgerClient.QueryBlockByTxID(fab.TransactionID(txId))
if err != nil {
fmt.Printf("QueryBlock return error: %s", err)
return nil, err
}
for i :=range transaction.TransactionActionList {
transaction.TransactionActionList[i].BlockNum=block.Header.Number
}
return transaction,nil
}
func QueryTransactionByTxIdJsonStr(txId string) (string,error){
transaction,err:=QueryTransactionByTxId(txId)
if err!=nil{
return "",err
}
jsonStr,err :=json.Marshal(transaction)
return string(jsonStr),err
}
jsonUtil.go
package services
import (
"encoding/json"
"fmt"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric-protos-go/ledger/rwset"
"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
"github.com/hyperledger/fabric-protos-go/msp"
"github.com/hyperledger/fabric-protos-go/peer"
"time"
)
func GetEnvelopeFromBlock(data []byte) (*common.Envelope, error){
var err error
env := &common.Envelope{}
if err = proto.Unmarshal(data, env); err != nil {
fmt.Printf("block unmarshal err: %s", err)
}
return env, nil
}
func GetTransactionFromEnvelopeDeep(rawEnvelope *common.Envelope)(*Transaction,error) {
rawPayload := &common.Payload{}
err := proto.Unmarshal(rawEnvelope.Payload, rawPayload)
if err != nil {
fmt.Printf("block unmarshal err: %s", err)
}
channelHeader := &common.ChannelHeader{}
err = proto.Unmarshal(rawPayload.Header.ChannelHeader, channelHeader)
if err != nil {
fmt.Printf("block unmarshal err: %s\n", err)
}
transactionObj := &peer.Transaction{}
err = proto.Unmarshal(rawPayload.Data, transactionObj)
if err != nil {
fmt.Printf("block unmarshal err: %s\n", err)
}
transactionActionList := []*TransactionAction{}
for i := range transactionObj.Actions {
transactionAction, err := GetTransactionActionFromTransactionDeep(transactionObj.Actions[i])
if err != nil {
fmt.Printf("block unmarshal err: %s\n", err)
}
transactionAction.TxId = channelHeader.TxId
transactionAction.Type = string(channelHeader.Type)
transactionAction.Timestamp = time.Unix(channelHeader.Timestamp.Seconds, 0).Format("2006-01-02 15:04:05")
transactionAction.ChannelId = channelHeader.ChannelId
transactionActionList = append(transactionActionList, transactionAction)
}
transaction :=Transaction{transactionActionList}
return &transaction,nil
}
func GetTransactionActionFromTransactionDeep(transactionAction *peer.TransactionAction)(*TransactionAction,error){
ChaincodeActionPayload := &peer.ChaincodeActionPayload{}
err := proto.Unmarshal(transactionAction.Payload, ChaincodeActionPayload)
if err != nil {
fmt.Printf("block unmarshal err: %s\n", err)
}
ProposalResponsePayload := &peer.ProposalResponsePayload{}
ChaincodeAction := &peer.ChaincodeAction{}
chaincodeId :=""
NsReadWriteSetList := []*rwset.NsReadWriteSet{}
ReadWriteSetList := []*kvrwset.KVRWSet{}
readSetList := []string{}
writeSetList := []string{}
if ChaincodeActionPayload.GetAction()!=nil{
err = proto.Unmarshal(ChaincodeActionPayload.Action.ProposalResponsePayload, ProposalResponsePayload)
if err != nil {
fmt.Printf("block unmarshal err: %s", err)
}
err = proto.Unmarshal(ProposalResponsePayload.Extension, ChaincodeAction)
if err != nil {
fmt.Printf("block unmarshal err: %s", err)
}
chaincodeId =ChaincodeAction.ChaincodeId.Name
TxReadWriteSet := &rwset.TxReadWriteSet{}
err = proto.Unmarshal(ChaincodeAction.Results, TxReadWriteSet)
if err != nil {
fmt.Printf("block unmarshal err: %s", err)
}
for i := range TxReadWriteSet.NsRwset {
ReadWriteSet := &kvrwset.KVRWSet{}
err = proto.Unmarshal(TxReadWriteSet.NsRwset[i].Rwset, ReadWriteSet)
if err != nil {
fmt.Printf("block unmarshal err: %s", err)
}
for i:=range ReadWriteSet.Reads{
readSetJsonStr,err :=json.Marshal(ReadWriteSet.Reads[i])
if err != nil {
fmt.Printf("block unmarshal err: %s", err)
}
readSetList = append(readSetList,string(readSetJsonStr))
}
for i:=range ReadWriteSet.Writes{
writeSetItem := map[string]interface{}{
"Key":ReadWriteSet.Writes[i].GetKey(),
"Value":string(ReadWriteSet.Writes[i].GetValue()),
"IsDelete":ReadWriteSet.Writes[i].GetIsDelete(),
}
writeSetJsonStr,err :=json.Marshal(writeSetItem)
if err != nil {
fmt.Printf("block unmarshal err: %s", err)
}
writeSetList = append(writeSetList,string(writeSetJsonStr))
}
ReadWriteSetList = append(ReadWriteSetList,ReadWriteSet)
NsReadWriteSetList = append(NsReadWriteSetList,TxReadWriteSet.NsRwset[i])
}
}else{
chaincodeId = "没有交易数据"
}
endorsements := []string{}
if ChaincodeActionPayload.Action.GetEndorsements()!=nil{
for i := range ChaincodeActionPayload.Action.GetEndorsements() {
endorser := &msp.SerializedIdentity{}
err = proto.Unmarshal(ChaincodeActionPayload.Action.Endorsements[i].Endorser, endorser)
if err != nil {
fmt.Printf("block unmarshal err: %s", err)
}
endorsements = append(endorsements,string(endorser.Mspid))
}
}
transactionActionObj :=TransactionAction{
Endorsements: endorsements,
ChaincodeId: chaincodeId,
ReadSetList: readSetList,
WriteSetList: writeSetList,
}
return &transactionActionObj,nil
}
其他
func InitOSConfig(){
log.Println("============ 正在使用"+runtime.GOOS+"系统 ============")
switch runtime.GOOS {
case "windows":
ccpPath =windowCcpPath
credPath =windowCredPath
chainBrowserConfigPath = windowConfigPath
break
case "linux":
ccpPath =linuxCcpPath
credPath =linuxCredPath
chainBrowserConfigPath = linuxConfigPath
break
default:
ccpPath =linuxCcpPath
credPath =linuxCredPath
chainBrowserConfigPath = linuxConfigPath
break
}
}
name: "example-network"
version: 1.0.0
client:
organization: Org1
logging:
level: info
cryptoconfig:
path: ${GOPATH}/src/github.com/rightstar/fabric-client-config
credentialStore:
path: /tmp/example-store
cryptoStore:
path: /tmp/example-msp
BCCSP:
security:
enabled: true
default:
provider: "SW"
hashAlgorithm: "SHA2"
softVerify: true
level: 256
tlsCerts:
systemCertPool: false
client:
key:
path:
cert:
path:
channels:
mychannel:
peers:
peer0.org1.example.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true
policies:
queryChannelConfig:
minResponses: 1
maxTargets: 1
retryOpts:
attempts: 5
initialBackoff: 500ms
maxBackoff: 5s
backoffFactor: 2.0
discovery:
maxTargets: 2
retryOpts:
attempts: 4
initialBackoff: 500ms
maxBackoff: 5s
backoffFactor: 2.0
eventService:
resolverStrategy: PreferOrg
balancer: Random
blockHeightLagThreshold: 5
reconnectBlockHeightLagThreshold: 10
peerMonitorPeriod: 5s
organizations:
Org1:
mspid: Org1MSP
cryptoPath: peerOrganizations/org1.example.com/users/{username}@org1.example.com/msp
users:
Admin:
cert:
path: ${GOPATH}/src/github.com/rightstar/fabric-client-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/cert.pem
User1:
cert:
path: ${GOPATH}/src/github.com/rightstar/fabric-client-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/signcerts/cert.pem
peers:
- peer0.org1.example.com
certificateAuthorities:
- ca.org1.example.com
- tlsca.org1.example.com
Org2:
mspid: Org2MSP
cryptoPath: peerOrganizations/org2.example.com/users/{username}@org2.example.com/msp
peers:
- peer0.org2.example.com
certificateAuthorities:
- ca.org2.example.com
orderers:
orderer.example.com:
url: localhost:7050
grpcOptions:
ssl-target-name-override: orderer.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
allow-insecure: false
tlsCACerts:
path: ${GOPATH}/src/github.com/rightstar/fabric-client-config/ordererOrganizations/example.com/msp/tlscacerts/tlsca.example.com-cert.pem
peers:
peer0.org1.example.com:
url: grpcs://localhost:7051
eventUrl: grpcs://localhost:7053
grpcOptions:
ssl-target-name-override: peer0.org1.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
allow-insecure: false
tlsCACerts:
path: ${GOPATH}/src/github.com/rightstar/fabric-client-config/peerOrganizations/org1.example.com/msp/tlscacerts/ca.crt
certificateAuthorities:
ca.org1.example.com:
url: https://localhost:7054
tlsCACerts:
path: ${GOPATH}/src/github.com/rightstar/fabric-client-config/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem
registrar:
enrollId: admin
enrollSecret: adminpw
caName: ca.org1.example.com
entityMatchers:
peer:
- pattern: (\w*)peer0.org1.example.com(\w*)
urlSubstitutionExp: grpcs://localhost:7051
eventUrlSubstitutionExp: grpcs://localhost:7053
sslTargetOverrideUrlSubstitutionExp: peer0.org1.example.com
mappedHost: peer0.org1.example.com
orderer:
- pattern: (\w*)orderer.example.com(\w*)
urlSubstitutionExp: grpcs://localhost:7050
sslTargetOverrideUrlSubstitutionExp: orderer.example.com
mappedHost: orderer.example.com
certificateAuthorities:
- pattern: (\w*)ca.org1.example.com(\w*)
urlSubstitutionExp: http://localhost:7054
mappedHost: ca.org1.example.com
后续
完整项目代码请关注作者更新
|