fabric SDK提供了很多对channel、peer等的操作接口,这些接口实际上是调用fabric源码中预置的系统chain code,即scc,源码所在目录为 core/scc/。从之前版本代码中可看出,共有五个,但目前1.2版本源码目录中仅有三个,分别是cscc、lscc和qscc。
//see systemchaincode_test.go for an example using "sample_syscc"
var systemChaincodes = []*SystemChaincode{
{
Enabled: true,
Name: "cscc",
Path: "github.com/hyperledger/fabric/core/scc/cscc",
InitArgs: [][]byte{[]byte("")},
Chaincode: &cscc.PeerConfiger{},
InvokableExternal: true, // cscc is invoked to join a channel
},
{
Enabled: true,
Name: "lscc",
Path: "github.com/hyperledger/fabric/core/scc/lscc",
InitArgs: [][]byte{[]byte("")},
Chaincode: lscc.NewLifeCycleSysCC(),
InvokableExternal: true, // lscc is invoked to deploy new chaincodes
InvokableCC2CC: true, // lscc can be invoked by other chaincodes
},
{
Enabled: true,
Name: "escc",
Path: "github.com/hyperledger/fabric/core/scc/escc",
InitArgs: [][]byte{[]byte("")},
Chaincode: &escc.EndorserOneValidSignature{},
},
{
Enabled: true,
Name: "vscc",
Path: "github.com/hyperledger/fabric/core/scc/vscc",
InitArgs: [][]byte{[]byte("")},
Chaincode: &vscc.ValidatorOneValidSignature{},
},
{
Enabled: true,
Name: "qscc",
Path: "github.com/hyperledger/fabric/core/chaincode/qscc",
InitArgs: [][]byte{[]byte("")},
Chaincode: &qscc.LedgerQuerier{},
InvokableExternal: true, // qscc can be invoked to retrieve blocks
InvokableCC2CC: true, // qscc can be invoked to retrieve blocks also by a cc
},
}
SDK提供了动态安装和更新chain code功能,但是源码中设定scc不允许动态安装与修改,其代码如下:
if lscc.sccprovider.IsSysCC(cds.ChaincodeSpec.ChaincodeId.Name) {
return errors.Errorf("cannot install: %s is the name of a system chaincode", cds.ChaincodeSpec.ChaincodeId.Name)
}
下面分别对已有scc进行简单的介绍。
Query System Chain Code,预置的一些区块查询cc。有对当前区块高度的查询以及一些通过不同参数查询Block详情的方法。block包含签名、transaction输入输出等等信息,它比在couchdb(之前开发fabric项目选用的是couchdb)所展现的数据更全面,下面通过block的查询源码来看block的存储位置以及获取方式。
// constants for indexable attributes
const (
IndexableAttrBlockNum = IndexableAttr("BlockNum")
IndexableAttrBlockHash = IndexableAttr("BlockHash")
IndexableAttrTxID = IndexableAttr("TxID")
IndexableAttrBlockNumTranNum = IndexableAttr("BlockNumTranNum")
IndexableAttrBlockTxID = IndexableAttr("BlockTxID")
IndexableAttrTxValidationCode = IndexableAttr("TxValidationCode")
)
// BlockStoreProvider provides an handle to a BlockStore
type BlockStoreProvider interface {
CreateBlockStore(ledgerid string) (BlockStore, error)
OpenBlockStore(ledgerid string) (BlockStore, error)
Exists(ledgerid string) (bool, error)
List() ([]string, error)
Close()
}
// BlockStore - an interface for persisting and retrieving blocks
// An implementation of this interface is expected to take an argument
// of type `IndexConfig` which configures the block store on what items should be indexed
type BlockStore interface {
AddBlock(block *common.Block) error
GetBlockchainInfo() (*common.BlockchainInfo, error)
RetrieveBlocks(startNum uint64) (ledger.ResultsIterator, error)
RetrieveBlockByHash(blockHash []byte) (*common.Block, error)
RetrieveBlockByNumber(blockNum uint64) (*common.Block, error) // blockNum of math.MaxUint64 will return last block
RetrieveTxByID(txID string) (*common.Envelope, error)
RetrieveTxByBlockNumTranNum(blockNum uint64, tranNum uint64) (*common.Envelope, error)
RetrieveBlockByTxID(txID string) (*common.Block, error)
RetrieveTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error)
Shutdown()
}
上述代码便是查询对应的接口,主要是通过建立索引查询,由此猜测是存储于某个数据库中,但在之前项目开发时并无发现couchdb中存在类似数据,下面选取其中一个接口继续向下研究。
func (index *blockIndex) getBlockLocByHash(blockHash []byte) (*fileLocPointer, error) {
if _, ok := index.indexItemsMap[blkstorage.IndexableAttrBlockHash]; !ok {
return nil, blkstorage.ErrAttrNotIndexed
}
b, err := index.db.Get(constructBlockHashKey(blockHash))
if err != nil {
return nil, err
}
if b == nil {
return nil, blkstorage.ErrNotFoundInIndex
}
blkLoc := &fileLocPointer{}
blkLoc.unmarshal(b)
return blkLoc, nil
}
type blockIndex struct {
indexItemsMap map[blkstorage.IndexableAttr]bool
db *leveldbhelper.DBHandle
}
显然,是对某个数据库进行基于索引的查询,但是blockIndex内的db是写死的leveldb,难道说无论配置中选择哪个数据库,它都会自启一个内置的leveldb进行block的存储?从我目前所了解的信息无法得出确切的结论,下一个专题这个问题进行探讨。
Life Cycle System Chaincode,主要提供chaincode的curd操作。chiancode安装分为两步,install和deploy或update。install需要管理员权限,对上传的chain code的基本信息(名称、版本等)进行简单校验后存放于特定目录,且生成的文件名格式为cc_name.cc_version,该目录及文件名设置代码如下:
// GetCCsPath returns the path where chaincodes are installed
func GetCCsPath() string {
return config.GetPath("peer.fileSystemPath") + string(filepath.Separator) + "chaincodes"
}
path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion)
deploy或update由调用方决定,其接口并不会根据已有信息来判断是否是更新。
var escc []byte
if len(args) > 4 && len(args[4]) > 0 {
escc = args[4]
} else {
escc = []byte("escc")
}
var vscc []byte
if len(args) > 5 && len(args[5]) > 0 {
vscc = args[5]
} else {
vscc = []byte("vscc")
}
神秘的escc、vscc出现了,在deployOrUpdate接口内作为参数传递,显然escc和vscc被独立了出来,通过文档了解到其提供了签名(验证策略)和state相关的依赖项,可以自己实现接口,编译成so文件引用(详细信息待研究)。经过一些参数判断及cc名称(名称为key)及版本配置等,最后会将需要实例化的cc核心信息作为一个block进行存储,在数据库中collection nam为channelName_lscc(_lscc固定)。
综上所述,真正生效的cc为根据区块链上的cc信息去检索本地文件获取到的,peer节点下也保存所有上传过的不同版本的cc(不允许同名称同版本上传),后续的查询方法也是基于此。
Configure System Chaincode 主要是对配置区块configuration block的查询及修改等操作。