Fabric 1.0原始碼分析(5)Chaincode(鏈碼)體系總結
# Fabric 1.0原始碼筆記 之 Chaincode(鏈碼)
## 1、Chaincode概述
Chaincode,即鏈碼或智慧合約,程式碼分佈在protos/peer目錄、core/chaincode和core/common/ccprovider目錄,目錄結構如下:
* protos/peer目錄:
* chaincode.pb.go,ChaincodeDeploymentSpec、ChaincodeInvocationSpec結構體定義。
* core/chaincode目錄:
* platforms目錄,鏈碼的編寫語言平臺實現,如golang或java。
* platforms.go,Platform介面定義,及部分工具函式。
* java目錄,java語言平臺實現。
* golang目錄,golang語言平臺實現。
* core/common/ccprovider目錄:ccprovider相關實現。
## 2、protos相關結構體定義
### 2.1、ChaincodeDeploymentSpec結構體定義(用於Chaincode部署)
#### 2.1.1 ChaincodeDeploymentSpec結構體定義
```go
type ChaincodeDeploymentSpec struct {
ChaincodeSpec *ChaincodeSpec //ChaincodeSpec訊息
EffectiveDate *google_protobuf1.Timestamp
CodePackage []byte //鏈碼檔案打包
ExecEnv ChaincodeDeploymentSpec_ExecutionEnvironment //鏈碼執行環境,DOCKER或SYSTEM
}
type ChaincodeDeploymentSpec_ExecutionEnvironment int32
const (
ChaincodeDeploymentSpec_DOCKER ChaincodeDeploymentSpec_ExecutionEnvironment = 0
ChaincodeDeploymentSpec_SYSTEM ChaincodeDeploymentSpec_ExecutionEnvironment = 1
)
//程式碼在protos/peer/chaincode.pb.go
```
#### 2.1.2、ChaincodeSpec結構體定義
```go
type ChaincodeSpec struct {
Type ChaincodeSpec_Type //鏈碼的編寫語言,GOLANG、JAVA
ChaincodeId *ChaincodeID //ChaincodeId,鏈碼路徑、鏈碼名稱、鏈碼版本
Input *ChaincodeInput //鏈碼的具體執行引數資訊
Timeout int32
}
type ChaincodeSpec_Type int32
const (
ChaincodeSpec_UNDEFINED ChaincodeSpec_Type = 0
ChaincodeSpec_GOLANG ChaincodeSpec_Type = 1
ChaincodeSpec_NODE ChaincodeSpec_Type = 2
ChaincodeSpec_CAR ChaincodeSpec_Type = 3
ChaincodeSpec_JAVA ChaincodeSpec_Type = 4
)
type ChaincodeID struct {
Path string
Name string
Version string
}
type ChaincodeInput struct { //鏈碼的具體執行引數資訊
Args [][]byte
}
//程式碼在protos/peer/chaincode.pb.go
```
### 2.2、ChaincodeInvocationSpec結構體定義
```go
type ChaincodeInvocationSpec struct {
ChaincodeSpec *ChaincodeSpec //參考本文2.2
IdGenerationAlg string
}
//程式碼在protos/peer/chaincode.pb.go
```
## 3、ccprovider目錄相關實現
### 3.1、ChaincodeData結構體
```go
type ChaincodeData struct {
Name string
Version string
Escc string
Vscc string
Policy []byte //chaincode 例項的背書策略
Data []byte
Id []byte
InstantiationPolicy []byte //例項化策略
}
//獲取ChaincodeData,優先從快取中讀取
func GetChaincodeData(ccname string, ccversion string) (*ChaincodeData, error)
//程式碼在core/common/ccprovider/ccprovider.go
```
func GetChaincodeData(ccname string, ccversion string) (*ChaincodeData, error)程式碼如下:
```go
var ccInfoFSProvider = &CCInfoFSImpl{}
var ccInfoCache = NewCCInfoCache(ccInfoFSProvider)
func GetChaincodeData(ccname string, ccversion string) (*ChaincodeData, error) {
//./peer/node/start.go: ccprovider.EnableCCInfoCache()
//如果啟用ccInfoCache,優先從快取中讀取ChaincodeData
if ccInfoCacheEnabled {
return ccInfoCache.GetChaincodeData(ccname, ccversion)
}
ccpack, err := ccInfoFSProvider.GetChaincode(ccname, ccversion)
return ccpack.GetChaincodeData(), nil
}
//程式碼在core/common/ccprovider/ccprovider.go
```
### 3.2、ccInfoCacheImpl結構體
```go
type ccInfoCacheImpl struct {
sync.RWMutex
cache map[string]*ChaincodeData //ChaincodeData
cacheSupport CCCacheSupport
}
//構造ccInfoCacheImpl
func NewCCInfoCache(cs CCCacheSupport) *ccInfoCacheImpl
//獲取ChaincodeData,優先從c.cache中獲取,如果c.cache中沒有,則從c.cacheSupport(即CCInfoFSImpl)中獲取並寫入c.cache
func (c *ccInfoCacheImpl) GetChaincodeData(ccname string, ccversion string) (*ChaincodeData, error)
//程式碼在core/common/ccprovider/ccinfocache.go
```
func (c *ccInfoCacheImpl) GetChaincodeData(ccname string, ccversion string) (*ChaincodeData, error) 程式碼如下:
```go
func (c *ccInfoCacheImpl) GetChaincodeData(ccname string, ccversion string) (*ChaincodeData, error) {
key := ccname + "/" + ccversion
c.RLock()
ccdata, in := c.cache[key] //優先從c.cache中獲取
c.RUnlock()
if !in { //如果c.cache中沒有
var err error
//從c.cacheSupport中獲取
ccpack, err := c.cacheSupport.GetChaincode(ccname, ccversion)
c.Lock()
//並寫入c.cache
ccdata = ccpack.GetChaincodeData()
c.cache[key] = ccdata
c.Unlock()
}
return ccdata, nil
}
//程式碼在core/common/ccprovider/ccinfocache.go
```
### 3.3、CCCacheSupport介面定義及實現
#### 3.3.1、CCCacheSupport介面定義
```go
type CCCacheSupport interface {
//獲取Chaincode包
GetChaincode(ccname string, ccversion string) (CCPackage, error)
}
//程式碼在core/common/ccprovider/ccprovider.go
```
#### 3.3.2、CCCacheSupport介面實現(即CCInfoFSImpl結構體)
```go
type CCInfoFSImpl struct{}
//從檔案系統中讀取並構造CDSPackage或SignedCDSPackage
func (*CCInfoFSImpl) GetChaincode(ccname string, ccversion string) (CCPackage, error) {
cccdspack := &CDSPackage{}
_, _, err := cccdspack.InitFromFS(ccname, ccversion)
if err != nil {
//try signed CDS
ccscdspack := &SignedCDSPackage{}
_, _, err = ccscdspack.InitFromFS(ccname, ccversion)
if err != nil {
return nil, err
}
return ccscdspack, nil
}
return cccdspack, nil
}
//將ChaincodeDeploymentSpec序列化後寫入檔案系統
func (*CCInfoFSImpl) PutChaincode(depSpec *pb.ChaincodeDeploymentSpec) (CCPackage, error) {
buf, err := proto.Marshal(depSpec)
cccdspack := &CDSPackage{}
_, err := cccdspack.InitFromBuffer(buf)
err = cccdspack.PutChaincodeToFS()
}
//程式碼在core/common/ccprovider/ccprovider.go
```
### 3.4、CCPackage介面定義及實現
#### 3.4.1、CCPackage介面定義
```go
type CCPackage interface {
//從位元組初始化包
InitFromBuffer(buf []byte) (*ChaincodeData, error)
//從檔案系統初始化包
InitFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error)
//將chaincode包寫入檔案系統
PutChaincodeToFS() error
//從包中獲取ChaincodeDeploymentSpec
GetDepSpec() *pb.ChaincodeDeploymentSpec
//從包中獲取序列化的ChaincodeDeploymentSpec
GetDepSpecBytes() []byte
//校驗ChaincodeData
ValidateCC(ccdata *ChaincodeData) error
//包轉換為proto.Message
GetPackageObject() proto.Message
//獲取ChaincodeData
GetChaincodeData() *ChaincodeData
//基於包計算獲取chaincode Id
GetId() []byte
}
//程式碼在core/common/ccprovider/ccprovider.go
```
#### 3.4.2、CCPackage介面實現(CDSPackage)
```go
type CDSData struct {
CodeHash []byte //ChaincodeDeploymentSpec.CodePackage雜湊
MetaDataHash []byte //ChaincodeSpec.ChaincodeId.Name和ChaincodeSpec.ChaincodeId.Version雜湊
}
type CDSPackage struct {
buf []byte //ChaincodeDeploymentSpec雜湊
depSpec *pb.ChaincodeDeploymentSpec //ChaincodeDeploymentSpec
data *CDSData
datab []byte
id []byte //id為CDSData.CodeHash和CDSData.MetaDataHash求雜湊
}
//獲取ccpack.id
func (ccpack *CDSPackage) GetId() []byte
//獲取ccpack.depSpec
func (ccpack *CDSPackage) GetDepSpec() *pb.ChaincodeDeploymentSpec
//獲取ccpack.buf,即ChaincodeDeploymentSpec雜湊
func (ccpack *CDSPackage) GetDepSpecBytes() []byte
//獲取ccpack.depSpec
func (ccpack *CDSPackage) GetPackageObject() proto.Message
//構造ChaincodeData
func (ccpack *CDSPackage) GetChaincodeData() *ChaincodeData
//獲取ChaincodeDeploymentSpec雜湊、CDSData、id
func (ccpack *CDSPackage) getCDSData(cds *pb.ChaincodeDeploymentSpec) ([]byte, []byte, *CDSData, error)
//校驗CDSPackage和ChaincodeData
func (ccpack *CDSPackage) ValidateCC(ccdata *ChaincodeData) error
//[]byte反序列化為ChaincodeDeploymentSpec,構造CDSPackage,進而構造ChaincodeData
func (ccpack *CDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, error)
//從檔案系統中獲取ChaincodeData,即buf, err := GetChaincodePackage(ccname, ccversion)和_, err = ccpack.InitFromBuffer(buf)
func (ccpack *CDSPackage) InitFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error)
//ccpack.buf寫入檔案,檔名為/var/hyperledger/production/chaincodes/Name.Version
func (ccpack *CDSPackage) PutChaincodeToFS() error
//程式碼在core/common/ccprovider/cdspackage.go
```
### 3.5、CCContext結構體
```go
type CCContext struct { //ChaincodeD上下文
ChainID string
Name string
Version string
TxID string
Syscc bool
SignedProposal *pb.SignedProposal
Proposal *pb.Proposal
canonicalName string
}
//構造CCContext
func NewCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) *CCContext
//name + ":" + version
func (cccid *CCContext) GetCanonicalName() string
//程式碼在core/common/ccprovider/ccprovider.go
```
## 4、chaincode目錄相關實現
### 4.1、ChaincodeProviderFactory介面定義及實現
#### 4.1.1、ChaincodeProviderFactory介面定義
```go
type ChaincodeProviderFactory interface {
//構造ChaincodeProvider例項
NewChaincodeProvider() ChaincodeProvider
}
func RegisterChaincodeProviderFactory(ccfact ChaincodeProviderFactory) {
ccFactory = ccfact
}
func GetChaincodeProvider() ChaincodeProvider {
return ccFactory.NewChaincodeProvider()
}
//程式碼在core/common/ccprovider/ccprovider.go
```
#### 4.1.2、ChaincodeProviderFactory介面實現
```go
type ccProviderFactory struct {
}
func (c *ccProviderFactory) NewChaincodeProvider() ccprovider.ChaincodeProvider {
return &ccProviderImpl{}
}
func init() {
ccprovider.RegisterChaincodeProviderFactory(&ccProviderFactory{})
}
//程式碼在core/chaincode/ccproviderimpl.go
```
### 4.2、ChaincodeProvider介面定義及實現
#### 4.2.1、ChaincodeProvider介面定義
```go
type ChaincodeProvider interface {
GetContext(ledger ledger.PeerLedger) (context.Context, error)
GetCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) interface{}
GetCCValidationInfoFromLSCC(ctxt context.Context, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, chainID string, chaincodeID string) (string, []byte, error)
ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) (*pb.Response, *pb.ChaincodeEvent, error)
Execute(ctxt context.Context, cccid interface{}, spec interface{}) (*pb.Response, *pb.ChaincodeEvent, error)
ExecuteWithErrorFilter(ctxt context.Context, cccid interface{}, spec interface{}) ([]byte, *pb.ChaincodeEvent, error)
Stop(ctxt context.Context, cccid interface{}, spec *pb.ChaincodeDeploymentSpec) error
ReleaseContext()
}
//程式碼在core/common/ccprovider/ccprovider.go
```
#### 4.2.2、ChaincodeProvider介面實現
```go
type ccProviderImpl struct {
txsim ledger.TxSimulator //交易模擬器
}
type ccProviderContextImpl struct {
ctx *ccprovider.CCContext
}
//獲取context.Context,新增TXSimulatorKey繫結c.txsim
func (c *ccProviderImpl) GetContext(ledger ledger.PeerLedger) (context.Context, error)
//構造CCContext,並構造ccProviderContextImpl
func (c *ccProviderImpl) GetCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) interface{}
//呼叫GetChaincodeDataFromLSCC(ctxt, txid, signedProp, prop, chainID, chaincodeID)獲取ChaincodeData中Vscc和Policy
func (c *ccProviderImpl) GetCCValidationInfoFromLSCC(ctxt context.Context, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, chainID string, chaincodeID string) (string, []byte, error)
//呼叫ExecuteChaincode(ctxt, cccid.(*ccProviderContextImpl).ctx, args)執行上下文中指定的鏈碼
func (c *ccProviderImpl) ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) (*pb.Response, *pb.ChaincodeEvent, error)
//呼叫Execute(ctxt, cccid.(*ccProviderContextImpl).ctx, spec)
func (c *ccProviderImpl) Execute(ctxt context.Context, cccid interface{}, spec interface{}) (*pb.Response, *pb.ChaincodeEvent, error)
//呼叫ExecuteWithErrorFilter(ctxt, cccid.(*ccProviderContextImpl).ctx, spec)
func (c *ccProviderImpl) ExecuteWithErrorFilter(ctxt context.Context, cccid interface{}, spec interface{}) ([]byte, *pb.ChaincodeEvent, error)
//呼叫theChaincodeSupport.Stop(ctxt, cccid.(*ccProviderContextImpl).ctx, spec)
func (c *ccProviderImpl) Stop(ctxt context.Context, cccid interface{}, spec *pb.ChaincodeDeploymentSpec) error
//呼叫c.txsim.Done()
func (c *ccProviderImpl) ReleaseContext() {
//程式碼在core/chaincode/ccproviderimpl.go
```
### 4.3、ChaincodeSupport結構體
ChaincodeSupport更詳細內容,參考:[Fabric 1.0原始碼筆記 之 Chaincode(鏈碼) #ChaincodeSupport(鏈碼支援服務端)](ChaincodeSupport.md)
### 4.4、ExecuteChaincode函式(執行鏈碼)
執行鏈碼上下文中指定的鏈碼。
```go
func ExecuteChaincode(ctxt context.Context, cccid *ccprovider.CCContext, args [][]byte) (*pb.Response, *pb.ChaincodeEvent, error) {
var spec *pb.ChaincodeInvocationSpec
var err error
var res *pb.Response
var ccevent *pb.ChaincodeEvent
spec, err = createCIS(cccid.Name, args) //構造ChaincodeInvocationSpec
res, ccevent, err = Execute(ctxt, cccid, spec)
return res, ccevent, err
}
//程式碼在core/chaincode/chaincodeexec.go
```
res, ccevent, err = Execute(ctxt, cccid, spec)程式碼如下:
```go
func Execute(ctxt context.Context, cccid *ccprovider.CCContext, spec interface{}) (*pb.Response, *pb.ChaincodeEvent, error) {
var err error
var cds *pb.ChaincodeDeploymentSpec
var ci *pb.ChaincodeInvocationSpec
cctyp := pb.ChaincodeMessage_INIT //初始化
if cds, _ = spec.(*pb.ChaincodeDeploymentSpec); cds == nil { //優先判斷ChaincodeDeploymentSpec
if ci, _ = spec.(*pb.ChaincodeInvocationSpec); ci == nil { //其次判斷ChaincodeInvocationSpec
panic("Execute should be called with deployment or invocation spec")
}
cctyp = pb.ChaincodeMessage_TRANSACTION //交易
}
_, cMsg, err := theChaincodeSupport.Launch(ctxt, cccid, spec)
var ccMsg *pb.ChaincodeMessage
ccMsg, err = createCCMessage(cctyp, cccid.TxID, cMsg)
resp, err := theChaincodeSupport.Execute(ctxt, cccid, ccMsg, theChaincodeSupport.executetimeout)
if resp.ChaincodeEvent != nil {
resp.ChaincodeEvent.ChaincodeId = cccid.Name
resp.ChaincodeEvent.TxId = cccid.TxID
}
if resp.Type == pb.ChaincodeMessage_COMPLETED {
res := &pb.Response{}
unmarshalErr := proto.Unmarshal(resp.Payload, res)
return res, resp.ChaincodeEvent, nil
}
}
//程式碼在core/chaincode
```
## 5、platforms(鏈碼的編寫語言平臺)
platforms更詳細內容,參考:[Fabric 1.0原始碼筆記 之 Chaincode(鏈碼) #platforms(鏈碼語言平臺)](platforms.md)
相關文章
- Fabric 1.0原始碼分析(3)Chaincode(鏈碼)原始碼AI
- Fabric 1.0原始碼分析(4)Chaincode(鏈碼)#platforms(鏈碼語言平臺)原始碼AIPlatform
- Fabric 1.0原始碼分析(42)scc(系統鏈碼)原始碼
- Fabric 1.0原始碼分析(34) Peer #peer chaincode命令及子命令實現原始碼AI
- Fabric 1.0原始碼分析(42)scc(系統鏈碼) #cscc(通道相關)原始碼
- Fabric 1.0原始碼分析(14) flogging(Fabric日誌系統)原始碼
- Fabric 1.0原始碼分析(25) Orderer原始碼
- Fabric 1.0原始碼分析(31) Peer原始碼
- Fabric 1.0原始碼分析(9)configtx(配置交易)體系介紹原始碼
- Fabric 1.0原始碼分析(29) Orderer #multichain(多鏈支援包)原始碼AI
- Fabric 1.0原始碼分析(40) Proposal(提案)原始碼
- Fabric 1.0原始碼分析(47)Fabric 1.0.4 go程式碼量統計原始碼Go
- Fabric 1.0原始碼分析(18) Ledger(賬本)原始碼
- Fabric 1.0原始碼分析(43) Tx(Transaction 交易)原始碼
- Fabric 1.0原始碼分析(13)events(事件服務)原始碼事件
- Fabric 1.0原始碼分析(26)Orderer #ledger(Orderer Ledger)原始碼
- Fabric 1.0原始碼分析(39) policy(背書策略)原始碼
- Fabric 1.0原始碼分析(45)gRPC(Fabric中註冊的gRPC Service)原始碼RPC
- Fabric 1.0原始碼分析(10)consenter(共識外掛)原始碼
- Fabric 1.0原始碼分析(15)gossip(流言演算法)原始碼Go演算法
- Fabric 1.0原始碼分析(23)LevelDB(KV資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(44)Tx #RWSet(讀寫集)原始碼
- Fabric 1.0原始碼分析(1)BCCSP(區塊鏈加密服務提供者)原始碼區塊鏈加密
- 區塊鏈教程Fabric1.0原始碼分析policy(背書策略)-兄弟連區塊鏈區塊鏈原始碼
- Fabric 1.0原始碼分析(6)configtx(配置交易) #ChannelConfig(通道配置)原始碼
- Fabric 1.0原始碼分析(20) Ledger #idStore(ledgerID資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(30) Orderer #BroadcastServer(Broadcast服務端)原始碼ASTServer服務端
- Fabric 1.0原始碼分析(35)Peer #EndorserServer(Endorser服務端)原始碼Server服務端
- Fabric 1.0原始碼分析(36) Peer #EndorserClient(Endorser客戶端)原始碼client客戶端
- Fabric 1.0原始碼分析(37) Peer #DeliverClient(Deliver客戶端)原始碼client客戶端
- Fabric 1.0原始碼分析(38) Peer #BroadcastClient(Broadcast客戶端)原始碼ASTclient客戶端
- Fabric 1.0原始碼分析(41)putils(protos/utils工具包)原始碼
- Fabric 1.0原始碼分析(2) blockfile(區塊檔案儲存)原始碼BloC
- Fabric 1.0原始碼分析(7)configtx(配置交易) #configtxgen(生成通道配置)原始碼
- Fabric 1.0原始碼分析(19) Ledger #statedb(狀態資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(21)Ledger #historydb(歷史資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(22)Ledger #blkstorage(block檔案儲存)原始碼BloC
- Fabric 1.0原始碼分析(27) Orderer #configupdate(處理通道配置更新)原始碼