fabric2.3版本源码记录_1
fabrci源码记录
writer:布羽
说明
本文件中的源码基于fabric2.3.x
阅读源码借鉴《Hyperledger Fabric核心技术》,书本基于v1.4
部分其他资料搜集于博客
源码简拼
MSP:Membership service provider 会员服务提供者
BCCSP:blockchain(前两个字母BC) cryptographic service provider 区域链加密服务提供者
ab:atomic broadcast原子(操作)广播
lscc:lifecycle(L) system(S) chaincode(CC)生命周期系统链码
Spec:Specification,规格标准,详细说明
KV:key-value 键-值
CDS:ChaincodeDeploymentSpec
CIS:ChaincodeInvocationSpec
mgmt:management
SW:software-based
AB:AtomicBroadcast
GB:genesis block,创世纪的block,也就是区域链中的第一个块
CC或cc:chaincode
SCC或scc:system chaincode
cscc:configer system chaincode
lscc:lifecycle system chaincode
escc:endorser system chaincode
vscc:validator system chaincode
qscc:querier system chaincode
alg:algorithm 算法
mcs:mspMessageCryptoService
mock:假装,学样子,模仿的意思,基本上是服务于xxx_test.go的,即用于测试的
Gossip:一种使分布结点达到状态最终一致的算法
attr:attribute
FsBlockStore:file system block store
vdb:versioned database 也就是状态数据库
RTEnv:runtime environment运行环境
pkcs11:pcks#11,一种公匙加密标准,有一套叫做Cryptoki的接口,是一组平台设备无关的API
MCS:mspMessageCryptoService,消息加密服务
sa:SecurityAdvisor
impl:implement,好多处XXX.go和XXXimpl.go是对应的,前者是用于接口或者定义的,后者是实现该接口或定义的
FSM:finite state machine 有限状态机
FS:filesystem 文件系统
blk:block
cli:command line interface 命令行界面
CFG:FABRIC_CFG_PATH中的,应该是config的意思
mgr:manager
cpinfo:checkpoint information,检查点信息
DevMode:development mode,开发模式
Reg:register,注册,登记
hdr:header
impl:implement
oid:ObjectIdentifier,对象标识符
ou或OU:organizational unit
CRL:certificate revocation list,废除证书列表
prop:proposal,申请,交易所发送的申请
ACL:Access Control List,访问控制列表
rwset:read/write set,读写集
tx,Tx:transaction,交易
CSP:cryptographic service provider,BCCSP的后三个字母,加密服务提供者
opt:option,选项
opts:options,多个选项
SKI:当前证书标识,所谓标识,一般是对公匙进行一下hash
AKI:签署方的SKI,也就是签署方的公匙标识
HSM:Hardware Security Modules
ks:KeyStore,Key存储,这个key指的是用于签名的公匙私匙
oid:OBJECT IDENTIFIER,对象身份标识
源码惯例
- common目录是其所在的层级中的公用的代码。A/common,则说明该common中的代码在A范围中公用,A/B/C/common,则说明该common中的代码在C目录中公用。
- mock目录是用于方便go测试文件(即众多的XXX_test.go)中进行测试所需要的模拟数据/环境等。研究源码的初始阶段可忽略该类目录。
- XXX.go与XXXimpl.go是定义与实现的配套代码。
- 同一事务分别存在与不同主题下。如protos目录下的peer与core目录下的peer都是peer相关的代码,但是相关主题的代码却分开放置。
- no-tls标有no-tls的,说明相关代码未使用安全传输协议(TLS)。
- util文件夹,一般都是该层级或该主题源码中具有辅助性,工具性的代码。
源码目录结构(v1.0)
- bcssp 加密服务代码目录
- common 全局公用代码目录
- core 核心功能代码目录
- docs 以.rst文件为核心,可编译生成文档。说明文档的目录
- events 事件代码目录,用于生产和消费信息
- examples 示例目录
- gossip 本意是绯闻的意思,是一种可达到去中心化,有一定容错能力且可达到最终一致的传播算法
- msp 会员服务代码目录
- orderer 就理解成orderer目录就好,orderer也算是区域链中的专用名词,用于消息的订阅与分发处理
- protos 原型目录,定义个各种原型和生成的对应的XXX.pb.go源码
- vendor 原意是商贩,在此就是存放go中使用的全部的各种第三方包
GRPC
在fabric2.0+版本中,所有的grpc通过引入fabric-protos-go包来实现
在1.4.4版本中,则实现grpc服务的proto全在相关服务的protos目录下
https://blog.csdn.net/zhongdahong/article/details/104659228
运维监控metrics
实现监控数据解析(部分文件路径已经不适用于fabric2.0) https://zhuanlan.zhihu.com/p/165524328?utm_source=wechat_session
shim
shim是peer和chaincode之间的中间层。shim被移到了fabric-chaincode-go中
链码chaincode
定义链码接口
shim\interfaces.go
// Chaincode interface must be implemented by all chaincodes. The fabric runs
// the transactions by calling these functions as specified.
type Chaincode interface {
// Init is called during Instantiate transaction after the chaincode container
// has been established for the first time, allowing the chaincode to
// initialize its internal data
Init(stub ChaincodeStubInterface) pb.Response
// Invoke is called to update or query the ledger in a proposal transaction.
// Updated state variables are not committed to the ledger until the
// transaction is committed.
Invoke(stub ChaincodeStubInterface) pb.Response
}
返回的类型response
peer\proposal_response.pb.go
可以看到返回状态码需要和HTTP状态一致,可以自定义
// A response with a representation similar to an HTTP response that can
// be used within another message.
type Response struct {
// A status code that should follow the HTTP status codes.
Status int32 `protobuf:"varint,1,opt,name=status,proto3" json:"status,omitempty"`
// A message associated with the response code.
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
// A payload that can be used to include metadata with this response.
Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
重点关注传入的参数类型ChaincodeStubInterface,shim\interfaces.go
主要用于为部署的链码提供访问和修改账本的方法。
type ChaincodeStubInterface interface {
...
}
里面的方法有以下作用
- 获取参数和函数名
- 获取交易和通道ID
- 调用其他链码(同通道的会更新读写集,不同通道的只有查询作用)
- 增删改查键的状态(实际只是体现在读写集,验证后更新账本以及状态库)、读写键的验证参数
- 范围查询(返回迭代器)、复合键查询、复合键创建和拆分(复合键在内部以0x00作为名称空间前缀)
- 获取查询结果(仅支持couchdb)、获取键值的历史记录(这一行的方法只应该在只读交易中使用,因为不会在验证阶段验证)
- 对于私有数据的相关操作,和键的操作类似的方法
- 获得签名者、提案临时数据、绑定数据、装饰数据、签名提案、交易时间戳(涉及到的数据结构有SignatureHeader、ChaincodeProposalPayload、ChaincodeInput、SignedProposal、ChannelHeader)
- 设置事件(涉及ChaincodeEvent、ChaincodeAction)
交易流程如下
ESCC和VSCC
在fabric1.2中。Escc和vscc被独立了出来(不能在core/scc目录下被找到了),提供了签名(验证策略)和state相关的依赖项,可以自己实现接口,编译成so文件引用。
但是可以参考core\endorser 以及core\committer,后续研究下
MSP
以上图片基于1.4版本仅供参考,源码使用2.0版本源码做分析
首先关注MSP的初始化
msp\mgmt\mgmt.go
//进行msp的初始化
LoadLocalMspWithType(dir string, bccspConfig *factory.FactoryOpts, mspID, mspType string) error
{
//获得配置信息msp.MSPConfig
conf<-msp.GetLocalMspConfigWithType(dir, bccspConfig, mspID, mspType)
//获得实现MSP接口的bccspmsp结构,setup是调用MSP接口的setup方法进行证书关联。
return GetLocalMSP(factory.GetDefault()).Setup(conf)
}
其中的 GetLocalMSP会调用loadLocalMSP去new一个msp实例(此时用的bccsp,根据msp类型会有不同的操作,默认使用bccspMSP,返回msp.MSP)这里面有个factory包(通过GetDefault()获得了BCCSP),之后在讨论完什么是BCCSP后再研究。
这个MSP提供了什么功能方法?
接下来需要分别观察msp包和bccsp包
首先观察
hyperledger-fabric\msp\msp.go
hyperledger-fabric\msp\mspimpl.go
//IdentityDeserializer由MSPManger和MSP共同实现
type IdentityDeserializer interface //身份证书反序列化接口
{
//反序列一个身份证明
DeserializeIdentity(serializedIdentity []byte) (Identity, error)
//检查证书格式
IsWellFormed(identity *msp.SerializedIdentity) error
}
//MSPManager是定义一个或多个MSP的管理器的接口。这实质上充当MSP调用的中介,并将MSP相关的调用路由到适当的MSP。
//此对象是不可变的,它只初始化一次就不会更改
type MSPManager interface {
//IdentityDeserializer接口需要由MSPManager实现
IdentityDeserializer
//根据配置信息设置MSP管理器实例
Setup(msps []MSP) error
//GetMSPs提供会员服务提供商列表
GetMSPs() (map[string]MSP, error)
}
type MSP interface {
// IdentityDeserializer interface needs to be implemented by MSP
IdentityDeserializer
// Setup the MSP instance according to configuration information
Setup(config *msp.MSPConfig) error
// GetVersion returns the version of this MSP
GetVersion() MSPVersion
// GetType returns the provider type
GetType() ProviderType
// GetIdentifier returns the provider identifier
GetIdentifier() (string, error)
// GetSigningIdentity returns a signing identity corresponding to the provided identifier
GetSigningIdentity(identifier *IdentityIdentifier) (SigningIdentity, error)
// GetDefaultSigningIdentity returns the default signing identity
GetDefaultSigningIdentity() (SigningIdentity, error)
// GetTLSRootCerts returns the TLS root certificates for this MSP
GetTLSRootCerts() [][]byte
// GetTLSIntermediateCerts returns the TLS intermediate root certificates for this MSP
GetTLSIntermediateCerts() [][]byte
//Validate检查提供的身份是否有效
Validate(id Identity) error
// SatisfiesPrincipal checks whether the identity matches
// the description supplied in MSPPrincipal. The check may
// involve a byte-by-byte comparison (if the principal is
// a serialized identity) or may require MSP validation
SatisfiesPrincipal(id Identity, principal *msp.MSPPrincipal) error
}
msp接口中主要注意以下四个方法IdentityDeserializer、Setup、Validate和SatisfiesPrincipal,其他get开头的方法都比较简单明了。
- IdentityDeserializer身份反序列化
- Setup证书关联
- Validate身份有效性验证
- SatisfiesPrincipal的作用是校验给定身份是否与principal中提供的描述匹配
MSP接口默认由bccspmsp实现,具体讲解见书P79
//hyperledger-fabric\msp\mspimpl.go
type bccspmsp struct {
// version specifies the behaviour of this msp
version MSPVersion
// The following function pointers are used to change the behaviour
// of this MSP depending on its version.
// internalSetupFunc is the pointer to the setup function
internalSetupFunc mspSetupFuncType
// internalValidateIdentityOusFunc is the pointer to the function to validate identity's OUs
internalValidateIdentityOusFunc validateIdentityOUsFuncType
// internalSatisfiesPrincipalInternalFunc is the pointer to the function to check if principals are satisfied
internalSatisfiesPrincipalInternalFunc satisfiesPrincipalInternalFuncType
// internalSetupAdmin is the pointer to the function that setup the administrators of this msp
internalSetupAdmin setupAdminInternalFuncType
// list of CA certs we trust
rootCerts []Identity
// list of intermediate certs we trust
intermediateCerts []Identity
// list of CA TLS certs we trust
tlsRootCerts [][]byte
// list of intermediate TLS certs we trust
tlsIntermediateCerts [][]byte
// certificationTreeInternalNodesMap whose keys correspond to the raw material
// (DER representation) of a certificate casted to a string, and whose values
// are boolean. True means that the certificate is an internal node of the certification tree.
// False means that the certificate corresponds to a leaf of the certification tree.
certificationTreeInternalNodesMap map[string]bool
// list of signing identities
signer SigningIdentity
// list of admin identities
admins []Identity
// the crypto provider
bccsp bccsp.BCCSP
// the provider identifier for this MSP
name string
// verification options for MSP members
opts *x509.VerifyOptions
// list of certificate revocation lists
CRL []*pkix.CertificateList
// list of OUs
ouIdentifiers map[string][][]byte
// cryptoConfig contains
cryptoConfig *m.FabricCryptoConfig
// NodeOUs configuration
ouEnforcement bool
// These are the OUIdentifiers of the clients, peers, admins and orderers.
// They are used to tell apart these entities
clientOU, peerOU, adminOU, ordererOU *OUIdentifier
}
可以看到,密码学相关的服务由bccsp提供,其中最重要的成员为BCCSP,封装了常用的加密标准和算法。
hyperledger-fabric\bccsp\bccsp.go
type BCCSP interface {
// KeyGen generates a key using opts.
KeyGen(opts KeyGenOpts) (k Key, err error)
// KeyDeriv derives a key from k using opts.
// The opts argument should be appropriate for the primitive used.
KeyDeriv(k Key, opts KeyDerivOpts) (dk Key, err error)
// KeyImport imports a key from its raw representation using opts.
// The opts argument should be appropriate for the primitive used.
KeyImport(raw interface{}, opts KeyImportOpts) (k Key, err error)
// GetKey returns the key this CSP associates to
// the Subject Key Identifier ski.
GetKey(ski []byte) (k Key, err error)
// Hash hashes messages msg using options opts.
// If opts is nil, the default hash function will be used.
Hash(msg []byte, opts HashOpts) (hash []byte, err error)
// GetHash returns and instance of hash.Hash using options opts.
// If opts is nil, the default hash function will be returned.
GetHash(opts HashOpts) (h hash.Hash, err error)
// Sign signs digest using key k.
// The opts argument should be appropriate for the algorithm used.
//
// Note that when a signature of a hash of a larger message is needed,
// the caller is responsible for hashing the larger message and passing
// the hash (as digest).
Sign(k Key, digest []byte, opts SignerOpts) (signature []byte, err error)
// Verify verifies signature against key k and digest
// The opts argument should be appropriate for the algorithm used.
Verify(k Key, signature, digest []byte, opts SignerOpts) (valid bool, err error)
// Encrypt encrypts plaintext using key k.
// The opts argument should be appropriate for the algorithm used.
Encrypt(k Key, plaintext []byte, opts EncrypterOpts) (ciphertext []byte, err error)
// Decrypt decrypts ciphertext using key k.
// The opts argument should be appropriate for the algorithm used.
Decrypt(k Key, ciphertext []byte, opts DecrypterOpts) (plaintext []byte, err error)
}
基于BCCSP的msp实例化由newBccspMsp实现(hyperledger-fabric\msp\mspimpl.go)
func newBccspMsp(version MSPVersion, defaultBCCSP bccsp.BCCSP) (MSP, error)
newBccspMsp根据传入的MSP版本号不同,采用的证书关联、验证策略等会有所不同,最后返回一个MSP。
而MSP采用的BCCSP也是传入newBccspMsp的参数之一。
newBccspMsp会在几个地方被用到,其本质作用就是实例化一个msp并返回。
最典型的被用到的场景就是一开始讨论到的LoadLocalMspWithType。
调用逻辑:LoadLocalMspWithType->GetLocalMSP->loadLocalMSP->New->newBccspMsp
注意:其中的msp.New在hyperledger-fabric\msp\factory.go
另外还在NewBccspMspWithKeyStore(hyperledger-fabric\msp\mspimpl.go)会调用newBccspMsp
// NewBccspMspWithKeyStore allows to create a BCCSP-based MSP whose underlying
// crypto material is available through the passed keystore
func NewBccspMspWithKeyStore(version MSPVersion, keyStore bccsp.KeyStore, bccsp bccsp.BCCSP) (MSP, error) {
thisMSP, err := newBccspMsp(version, bccsp)
if err != nil {
return nil, err
}
csp, err := sw.NewWithParams(
factory.GetDefaultOpts().SW.Security,
factory.GetDefaultOpts().SW.Hash,
keyStore)
if err != nil {
return nil, err
}
thisMSP.(*bccspmsp).bccsp = csp
return thisMSP, nil
}
查看调用这个实例化函数的地方,传入的bccsp参数都是通过下面这个语句生成的
//ks是传给NewBccspMspWithKeyStore的bccsp.KeyStore
ks, err := sw.NewFileBasedKeyStore(nil, filepath.Join(dir, "keystore"), true)
//cryptoProvider是要传给NewBccspMspWithKeyStore的bccsp.BCCSP
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
到这里先不继续深究了,盲猜keystore是用于选择是否将密钥保存在硬盘中(不保存的话关机就丢失),dir参数即想要存储的文件路径。回去接着关注BCCSP
首先明确BCCSP的两种实现有SW和PKCS11。其中SW为基于软件的BCCSP实现,支持的加密算法比较多。而pkcs11借助了口令保护的数据库或者硬件设备。故主要关注SW即可。
在factory包中,定义了接口BCCSPFactory,它的两个实现方式为SW和PKCS11
hyperledger-fabric\bccsp\factory\factory.go
type BCCSPFactory interface {
// Name returns the name of this factory,例如“SW”
Name() string
// Get returns an instance of BCCSP using opts.返回BCCSP实例
Get(opts *FactoryOpts) (bccsp.BCCSP, error)
}
之前一开始提到的GetDefault(),也在这个包里。如果defaultBCCSP没有定义,则使用SW类型的bootBCCSP
hyperledger-fabric\bccsp\factory\factory.go
func GetDefault() bccsp.BCCSP {
if defaultBCCSP == nil {
logger.Debug("Before using BCCSP, please call InitFactories(). Falling back to bootBCCSP.")
bootBCCSPInitOnce.Do(func() {
var err error
bootBCCSP, err = (&SWFactory{}).Get(GetDefaultOpts())
if err != nil {
panic("BCCSP Internal error, failed initialization with GetDefaultOpts!")
}
})
return bootBCCSP
}
return defaultBCCSP
}
GetDefaultOpts()是用于返回BCCSP实例化时的默认配置(哈希家族、安全层数)
hyperledger-fabric\bccsp\factory\opts.go
func GetDefaultOpts() *FactoryOpts {
return &FactoryOpts{
Default: "SW",
SW: &SwOpts{
Hash: "SHA2",
Security: 256,
},
}
}
接下来主要关注SW的Get实现.
这个方法首先检查FileKeystore选项
- 若不为空,则用文件去存储密钥,路径swOpts.FileKeystore.KeyStorePath
- 若为空,则默认为临时存储密钥。
最后调用 sw.NewWithParams(swOpts.Security, swOpts.Hash, ks),返回根据配置实例化后的BCCSP
hyperledger-fabric\bccsp\factory\swfactory.go
// Get returns an instance of BCCSP using Opts.
func (f *SWFactory) Get(config *FactoryOpts) (bccsp.BCCSP, error) {
// Validate arguments
if config == nil || config.SW == nil {
return nil, errors.New("Invalid config. It must not be nil.")
}
swOpts := config.SW
var ks bccsp.KeyStore
switch {
case swOpts.FileKeystore != nil:
fks, err := sw.NewFileBasedKeyStore(nil, swOpts.FileKeystore.KeyStorePath, false)
if err != nil {
return nil, errors.Wrapf(err, "Failed to initialize software key store")
}
ks = fks
default:
// Default to ephemeral key store
ks = sw.NewDummyKeyStore()
}
return sw.NewWithParams(swOpts.Security, swOpts.Hash, ks)
}
接下来还可以查看sw包(hyperledger-fabric\bccsp\sw)看看具体如何实例化swbccsp的,swbccsp内部的结构是怎么样的。具体看书P83
那么最后再看看证书关联(Setup)具体涉及到哪些结构和方法
此时bccspmsp已经构建好了,但只有框架和加密算法,还需要关联证书,从而提供成员身份服务提供者的完整功能。
hyperledger-fabric\msp\mspimpl.go
func (msp *bccspmsp) Setup(conf1 *m.MSPConfig) error {
if conf1 == nil {
return errors.New("Setup error: nil conf reference")
}
// given that it's an msp of type fabric, extract the MSPConfig instance
conf := &m.FabricMSPConfig{}
err := proto.Unmarshal(conf1.Config, conf)
if err != nil {
return errors.Wrap(err, "failed unmarshalling fabric msp config")
}
// set the name for this msp
msp.name = conf.Name
mspLogger.Debugf("Setting up MSP instance %s", msp.name)
// setup
return msp.internalSetupFunc(conf)
}
最后的internalSetupFunc是bccspmsp结构体中的字段,由newBccspMsp函数根据参数MSP版本来赋值。
case MSPv1_4_3:
theMsp.internalSetupFunc = theMsp.setupV142
theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV142
theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalV142
theMsp.internalSetupAdmin = theMsp.setupAdminsV142
以MSPv1_4_3为例,观察setupV142
hyperledger-fabric\msp\mspimplsetup.go
func (msp *bccspmsp) setupV142(conf *m.FabricMSPConfig) error {
err := msp.preSetupV142(conf)
if err != nil {
return err
}
err = msp.postSetupV142(conf)
if err != nil {
return err
}
return nil
}
涉及两个函数,前者preSetupV142根据配置设置bccspmsp结构成员(在设置过程中也会进行证书链的检查),后者postSetupV142对管理员证书进行合法性验证。具体见书P87
这两个函数中会用到许多方法,CA证书相关(建立根证书池和中间证书池(预初始化msp.opts),检查证书链是否唯一(getIdentityFromCon),检查证书签名是否在椭圆曲线阶群范围内),管理员证书列表(通过getIdentityFromCon来读取证书生成身份ID,存入msp.admins),撤销证书列表,签名身份列表,证书的签名和验证,序列化和反序列化等等。
序列化可以再深入了解下,首先需要关注下identity。
这个结构体如下所见,存有一些指针,指向实例的身份id,证书,公钥,msp等等
hyperledger-fabric\msp\identities.go
type identity struct {
// id contains the identifier (MSPID and identity identifier) for this instance
id *IdentityIdentifier
// cert contains the x.509 certificate that signs the public key of this instance
cert *x509.Certificate
// this is the public key of this instance
pk bccsp.Key
// reference to the MSP that "owns" this identity
msp *bccspmsp
// validationMutex is used to synchronise memory operation
// over validated and validationErr
validationMutex sync.Mutex
// validated is true when the validateIdentity function
// has been called on this instance
validated bool
// validationErr contains the validation error for this
// instance. It can be read if validated is true
validationErr error
}
前面多次提到了getIdentityFromCon,它会调用newIdentity。
newIdentity有两个步骤,证书审查sanitizeCert,以及封装identity实例并返回。
那么接下来可以看看序列化操作了,它的接收器就是identity。大体步骤是先将证书编码,然后再加上Mspid,一起序列化(调用Marshal)。
hyperledger-fabric\msp\identities.go
func (id *identity) Serialize() ([]byte, error) {
pb := &pem.Block{Bytes: id.cert.Raw, Type: "CERTIFICATE"}
pemBytes := pem.EncodeToMemory(pb)
if pemBytes == nil {
return nil, errors.New("encoding of identity failed")
}
// We serialize identities by prepending the MSPID and appending the ASN.1 DER content of the cert
sId := &msp.SerializedIdentity{Mspid: id.id.Mspid, IdBytes: pemBytes}
idBytes, err := proto.Marshal(sId)
if err != nil {
return nil, errors.Wrapf(err, "could not marshal a SerializedIdentity structure for identity %s", id.id)
}
return idBytes, nil
}
相对应的,反序列化操作DeserializeIdentity大体步骤就是解码(调用Unmarshal),检查mspid,用得到的数据调用newIdentity。(反序列函数在hyperledger-fabric\msp\mspimpl.go)
现在,让我们回到最开头讲的MSP接口,我们还剩两个方法需要关注
- Validate身份有效性验证
- SatisfiesPrincipal的作用是校验给定身份是否与principal中提供的描述匹配
先看Validate
hyperledger-fabric\msp\mspimpl.go
func (msp *bccspmsp) Validate(id Identity) error {
mspLogger.Debugf("MSP %s validating identity", msp.name)
switch id := id.(type) {
// If this identity is of this specific type,
// this is how I can validate it given the
// root of trust this MSP has
case *identity:
return msp.validateIdentity(id)
default:
return errors.New("identity type not recognized")
}
}
只支持校验*identity,调用validateIdentity,大致步骤为
- 把id上锁,验证结束后解锁
- 获得id的证书链
- 检查证书是否被撤销
- 验证身份的组织单元信息是否合法
每个阶段的具体讲解见书P103
hyperledger-fabric\msp\mspimplvalidate.go
func (msp *bccspmsp) validateIdentity(id *identity) error {
id.validationMutex.Lock()
defer id.validationMutex.Unlock()
// return cached validation value if already validated
if id.validated {
return id.validationErr
}
id.validated = true
validationChain, err := msp.getCertificationChainForBCCSPIdentity(id)
if err != nil {
id.validationErr = errors.WithMessage(err, "could not obtain certification chain")
return id.validationErr
}
err = msp.validateIdentityAgainstChain(id, validationChain)
if err != nil {
id.validationErr = errors.WithMessage(err, "could not validate identity against certification chain")
return id.validationErr
}
err = msp.internalValidateIdentityOusFunc(id)
if err != nil {
id.validationErr = errors.WithMessage(err, "could not validate identity's OUs")
return id.validationErr
}
return nil
}
其中验证身份的组织单元信息这个函数基于msp版本而进行不同的赋值,最新版本的函数validateIdentityOUsV142会在旧版本的函数theMsp.validateIdentityOUsV1上多进行些操作
从下面的代码可以看出,validateIdentityOUsV1的步骤如下:
- 检查msp.ouIdentifiers长度是否大于0,
- 求msp.ouIdentifiers和传入id的组织单元列表的交集,比较的是组织单元相关的证书链的哈希
hyperledger-fabric\msp\mspimplvalidate.go
func (msp *bccspmsp) validateIdentityOUsV1(id *identity) error {
// Check that the identity's OUs are compatible with those recognized by this MSP,
// meaning that the intersection is not empty.
if len(msp.ouIdentifiers) > 0 {
found := false
for _, OU := range id.GetOrganizationalUnits() {
certificationIDs, exists := msp.ouIdentifiers[OU.OrganizationalUnitIdentifier]
if exists {
for _, certificationID := range certificationIDs {
if bytes.Equal(certificationID, OU.CertifiersIdentifier) {
found = true
break
}
}
}
}
if !found {
if len(id.GetOrganizationalUnits()) == 0 {
return errors.New("the identity certificate does not contain an Organizational Unit (OU)")
}
return errors.Errorf("none of the identity's organizational units %s are in MSP %s", OUIDs(id.GetOrganizationalUnits()), msp.name)
}
}
return nil
}
在validateIdentityOUsV142中会在开头调用validateIdentityOUsV1,之后会进行一系列enforcement相关的操作。这个enforcement具体的值是否开启nodeOU。nodeOU在官方文档解释是一种特殊的OU用法。普通的OU是将组织划分为多个组织单元,类似部门的概念。而nodeOU则是会给组织单元中的身份赋予一定的角色,每次去向CA注册的时候就可以将角色作为参数。
再关注下SatisfiesPrincipal。具体见书P106。分为两步(Principal:确定权限的附加属性)
- 搜集规则collectPrincipals,实际是递归函数,支持组合策略
- internalSatisfiesPrincipalInternalFunc(id, principal),也是根据msp版本不同进行赋值的。检查给定的身份是否与所有搜集的规则匹配。根据规则类型的不同(角色、身份、组织单元等等)从而采取不同的检查方法。 最新版本的调用链是 satisfiesPrincipalInternalV142->satisfiesPrincipalInternalV13->satisfiesPrincipalInternalPreV13
hyperledger-fabric\msp\mspimpl.go
// SatisfiesPrincipal returns nil if the identity matches the principal or an error otherwise
func (msp *bccspmsp) SatisfiesPrincipal(id Identity, principal *m.MSPPrincipal) error {
principals, err := collectPrincipals(principal, msp.GetVersion())
if err != nil {
return err
}
for _, principal := range principals {
err = msp.internalSatisfiesPrincipalInternalFunc(id, principal)
if err != nil {
return err
}
}
return nil
}
Principal相关的结构体如下,
PrincipalClassification描述princiapl的类型
Principal为承载类型的容器
fabric-proto-go\msp\msp_principal.pb.go
type MSPPrincipal struct {
// Classification describes the way that one should process
// Principal. An Classification value of "ByOrganizationUnit" reflects
// that "Principal" contains the name of an organization this MSP
// handles. A Classification value "ByIdentity" means that
// "Principal" contains a specific identity. Default value
// denotes that Principal contains one of the groups by
// default supported by all MSPs ("admin" or "member").
PrincipalClassification MSPPrincipal_Classification `protobuf:"varint,1,opt,name=principal_classification,json=principalClassification,proto3,enum=common.MSPPrincipal_Classification" json:"principal_classification,omitempty"`
// Principal completes the policy principal definition. For the default
// principal types, Principal can be either "Admin" or "Member".
// For the ByOrganizationUnit/ByIdentity values of Classification,
// PolicyPrincipal acquires its value from an organization unit or
// identity, respectively.
// For the Combined Classification type, the Principal is a marshalled
// CombinedPrincipal.
Principal []byte `protobuf:"bytes,2,opt,name=principal,proto3" json:"principal,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
类型有以下
type MSPPrincipal_Classification int32
const (
MSPPrincipal_ROLE MSPPrincipal_Classification = 0
// one of a member of MSP network, and the one of an
// administrator of an MSP network
MSPPrincipal_ORGANIZATION_UNIT MSPPrincipal_Classification = 1
// groupping of entities, per MSP affiliation
// E.g., this can well be represented by an MSP's
// Organization unit
MSPPrincipal_IDENTITY MSPPrincipal_Classification = 2
// identity
MSPPrincipal_ANONYMITY MSPPrincipal_Classification = 3
// an identity to be anonymous or nominal.
MSPPrincipal_COMBINED MSPPrincipal_Classification = 4
)
签名策略
签名策略通常包含两部分
- 背书签名的主体Principal(MSPPrincipal的成员),即期望的来源。签名策略只支持member和admin两种
- 门槛thsholdgate(字符表达式,表示策略需要达到的条件)
fabric-proto-go\common\policies.pb.go
type SignaturePolicy struct {
// Types that are valid to be assigned to Type:
// *SignaturePolicy_SignedBy
// *SignaturePolicy_NOutOf_
Type isSignaturePolicy_Type `protobuf_oneof:"Type"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
实现isSignaturePolicy_Type接口的结构有以下两个
type SignaturePolicy_SignedBy struct {
SignedBy int32 `protobuf:"varint,1,opt,name=signed_by,json=signedBy,proto3,oneof"`
}
type SignaturePolicy_NOutOf_ struct {
NOutOf *SignaturePolicy_NOutOf `protobuf:"bytes,2,opt,name=n_out_of,json=nOutOf,proto3,oneof"`
}
第一个,它的成员SignedBy表示实体在MSPPrinciapl列表的下标,指示了Principal来源。
第二个,它的成员的结构如下
type SignaturePolicy_NOutOf struct {
N int32 `protobuf:"varint,1,opt,name=n,proto3" json:"n,omitempty"`
Rules []*SignaturePolicy `protobuf:"bytes,2,rep,name=rules,proto3" json:"rules,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
- n表示需要多少个签名满足。
- Rules是个递归的结构,指示主体。如果Rules的类型是SignaturePolicy_SignedBy,那么递归终止。
对签名策略进行封装的结构为
type SignaturePolicyEnvelope struct {
Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
Rule *SignaturePolicy `protobuf:"bytes,2,opt,name=rule,proto3" json:"rule,omitempty"`
Identities []*msp.MSPPrincipal `protobuf:"bytes,3,rep,name=identities,proto3" json:"identities,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
最后再看看策略的编译
common\cauthdsl\cauthdsl.go
func compile(policy *cb.SignaturePolicy, identities []*mb.MSPPrincipal) (func([]msp.Identity, []bool) bool, error) {
...
}
它会根据签名策略构建返回一个匿名的评估函数,可以用这个函数去评估签名数组SignedData(从背书消息中得到)是否符合预定义的签名策略。大致步骤为:
检查签名策略的类型:
- 若为SignedBy,直接检查,检查完毕返回匿名函数;
- 若为NOutOf_,则递归检查。
检查的步骤为:
- 检查主体是否在主体列表中,
- 判断是否检查过策略,检查过的策略不会重复检查,(因为是递归的,可能会有很多重复的策略,所以没必要重新检查)
- 反序列化身份,并调用SatisfiesPrincipal,检查主体身份与规则的描述是否匹配,
- 调用Verify验证签名是否合法,
- 检查通过后设置已检查标志。