概述
peer chaincode instantiate命令用于对已安装(install)的链码实例化
介绍
instantiate命令定义
向命令instantiate添加flagList中的标记,分别代表,
Lang:链码语言(go,nodejs等)
ctor : json字符串参数(如:‘{args:[''init","a","100","b","200"]}’)
name:链码名称
channelID:通道
version:链码版本
policy:背书策略
escc:对交易结果的结构转换和签名背书
vscc:记账前对区块和交易进行验证
func instantiateCmd(cf *ChaincodeCmdFactory) *cobra.Command {
chaincodeInstantiateCmd = &cobra.Command{
Use: instantiateCmdName,
Short: fmt.Sprint(instantiateDesc),
Long: fmt.Sprint(instantiateDesc),
ValidArgs: []string{"1"},
RunE: func(cmd *cobra.Command, args []string) error {
return chaincodeDeploy(cmd, args, cf)
},
}
flagList := []string{
"lang",
"ctor",
"name",
"channelID",
"version",
"policy",
"escc",
"vscc",
}
attachFlags(chaincodeInstantiateCmd, flagList)
return chaincodeInstantiateCmd
}
命令执行过程
命令从chaincodeDeploy方法开始
func chaincodeDeploy(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory) error {
var err error
if cf == nil {
//获取ChaincodeCmdFactory
cf, err = InitCmdFactory(true, true)
if err != nil {
return err
}
}
//将所有信息发送完毕后关闭grpc连接
defer cf.BroadcastClient.Close()
//链码实例化流程
env, err := instantiate(cmd, cf)
if err != nil {
return err
}
//成功实例化后返回组合消息并签名的交易信封
if env != nil {
//发送交易信封内容到orderer排序
err = cf.BroadcastClient.Send(env)
}
return err
}
InitCmdFactory方法已经在peer chaincode install详细介绍。
instantiate过程
func instantiate(cmd *cobra.Command, cf *ChaincodeCmdFactory) (*protcommon.Envelope, error) {
//获取ChaincodeSpec
spec, err := getChaincodeSpec(cmd)
if err != nil {
return nil, err
}
//获取ChaincodeDeploymentSpec
cds, err := getChaincodeDeploymentSpec(spec, false)
if err != nil {
return nil, fmt.Errorf("Error getting chaincode code %s: %s", chainFuncName, err)
}
//从msp获取签名身份
creator, err := cf.Signer.Serialize()
if err != nil {
return nil, fmt.Errorf("Error serializing identity for %s: %s", cf.Signer.GetIdentifier(), err)
}
//根据ChaincodeDeploymentSpec创建部署的提案
prop, _, err := utils.CreateDeployProposalFromCDS(chainID, cds, creator, policyMarhsalled, []byte(escc), []byte(vscc))
if err != nil {
return nil, fmt.Errorf("Error creating proposal %s: %s", chainFuncName, err)
}
//对提案签名
var signedProp *pb.SignedProposal
signedProp, err = utils.GetSignedProposal(prop, cf.Signer)
if err != nil {
return nil, fmt.Errorf("Error creating signed proposal %s: %s", chainFuncName, err)
}
//背书节点对提案处理并返回提案响应
proposalResponse, err := cf.EndorserClient.ProcessProposal(context.Background(), signedProp)
if err != nil {
return nil, fmt.Errorf("Error endorsing %s: %s", chainFuncName, err)
}
if proposalResponse != nil {
// assemble a signed transaction (it's an Envelope message)
//组合消息并签名返回至客户端交易的信封
env, err := utils.CreateSignedTx(prop, cf.Signer, proposalResponse)
if err != nil {
return nil, fmt.Errorf("Could not assemble transaction, err %s", err)
}
return env, nil
}
return nil, nil
}
getChaincodeSpec(),getChaincodeDeploymentSpec()方法详情已经在hperledger fabric 1.0 源码分析之peer chaincode install介绍。看下签名身份的获取
//cf *ChaincodeCmdFactory结构的定义
type ChaincodeCmdFactory struct {
EndorserClient pb.EndorserClient
Signer msp.SigningIdentity
BroadcastClient common.BroadcastClient
}
//由上可知cf.Signer来源于msp.SigningIdentity
//msp.SigningIdentity的定义
type SigningIdentity interface {
// Extends Identity
Identity
// Sign the message
Sign(msg []byte) ([]byte, error)
// GetPublicVersion returns the public parts of this identity
GetPublicVersion() Identity
}
//SigningIdentity.Sign具体实现
func (id *signingidentity) Sign(msg []byte) ([]byte, error) {
//mspIdentityLogger.Infof("Signing message")
// Compute Hash
hashOpt, err := id.getHashOpt(id.msp.cryptoConfig.SignatureHashFamily)
if err != nil {
return nil, fmt.Errorf("Failed getting hash function options [%s]", err)
}
digest, err := id.msp.bccsp.Hash(msg, hashOpt)
if err != nil {
return nil, fmt.Errorf("Failed computing digest [%s]", err)
}
if len(msg) < 32 {
mspIdentityLogger.Debugf("Sign: plaintext: %X \n", msg)
} else {
mspIdentityLogger.Debugf("Sign: plaintext: %X...%X \n", msg[0:16], msg[len(msg)-16:])
}
mspIdentityLogger.Debugf("Sign: digest: %X \n", digest)
// Sign
return id.signer.Sign(rand.Reader, digest, nil)
}
返回来,看提案的创建(根据ChaincodeDeploymentSpec创建部署的提案)
func CreateDeployProposalFromCDS(chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte, policy []byte, escc []byte, vscc []byte) (*peer.Proposal, string, error) {
return createProposalFromCDS(chainID, cds, creator, policy, escc, vscc, "deploy")
}
func createProposalFromCDS(chainID string, msg proto.Message, creator []byte, policy []byte, escc []byte, vscc []byte, propType string) (*peer.Proposal, string, error) {
//in the new mode, cds will be nil, "deploy" and "upgrade" are instantiates.
var ccinp *peer.ChaincodeInput
var b []byte
var err error
if msg != nil {
b, err = proto.Marshal(msg)
if err != nil {
return nil, "", err
}
}
switch propType {
case "deploy":
fallthrough
case "upgrade":
cds, ok := msg.(*peer.ChaincodeDeploymentSpec)
if !ok || cds == nil {
return nil, "", fmt.Errorf("invalid message for creating lifecycle chaincode proposal from")
}
ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), []byte(chainID), b, policy, escc, vscc}}
case "install":
ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), b}}
}
//wrap the deployment in an invocation spec to lscc...
lsccSpec := &peer.ChaincodeInvocationSpec{
ChaincodeSpec: &peer.ChaincodeSpec{
Type: peer.ChaincodeSpec_GOLANG,
ChaincodeId: &peer.ChaincodeID{Name: "lscc"},
Input: ccinp}}
//...and get the proposal for it
return CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, chainID, lsccSpec, creator)
}
func CreateProposalFromCIS(typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, creator []byte) (*peer.Proposal, string, error) {
return CreateChaincodeProposal(typ, chainID, cis, creator)
}
func CreateChaincodeProposal(typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, creator []byte) (*peer.Proposal, string, error) {
return CreateChaincodeProposalWithTransient(typ, chainID, cis, creator, nil)
}
func CreateChaincodeProposalWithTransient(typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) {
// generate a random nonce
nonce, err := crypto.GetRandomNonce()
if err != nil {
return nil, "", err
}
// compute txid
txid, err := ComputeProposalTxID(nonce, creator)
if err != nil {
return nil, "", err
}
return CreateChaincodeProposalWithTxIDNonceAndTransient(txid, typ, chainID, cis, nonce, creator, transientMap)
}
func CreateChaincodeProposalWithTxIDNonceAndTransient(txid string, typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, nonce, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) {
ccHdrExt := &peer.ChaincodeHeaderExtension{ChaincodeId: cis.ChaincodeSpec.ChaincodeId}
ccHdrExtBytes, err := proto.Marshal(ccHdrExt)
if err != nil {
return nil, "", err
}
cisBytes, err := proto.Marshal(cis)
if err != nil {
return nil, "", err
}
ccPropPayload := &peer.ChaincodeProposalPayload{Input: cisBytes, TransientMap: transientMap}
ccPropPayloadBytes, err := proto.Marshal(ccPropPayload)
if err != nil {
return nil, "", err
}
// TODO: epoch is now set to zero. This must be changed once we
// get a more appropriate mechanism to handle it in.
var epoch uint64 = 0
timestamp := util.CreateUtcTimestamp()
hdr := &common.Header{ChannelHeader: MarshalOrPanic(&common.ChannelHeader{
Type: int32(typ),
TxId: txid,
Timestamp: timestamp,
ChannelId: chainID,
Extension: ccHdrExtBytes,
Epoch: epoch}),
SignatureHeader: MarshalOrPanic(&common.SignatureHeader{Nonce: nonce, Creator: creator})}
hdrBytes, err := proto.Marshal(hdr)
if err != nil {
return nil, "", err
}
return &peer.Proposal{Header: hdrBytes, Payload: ccPropPayloadBytes}, txid, nil
}
整个一系列的提案生成过程与peer chaincode install命令中生成安装提案的过程十分类似。通过源码比较可以发现,在创建提案时传入的参数不同,后续调用的方法相同
//源码位于github.com/hyperledger/fabric/protos/utils/proputils.go
//创建安装提案
func CreateInstallProposalFromCDS(ccpack proto.Message, creator []byte) (*peer.Proposal, string, error) {
return createProposalFromCDS("", ccpack, creator, nil, nil, nil, "install")
}
//创建部署提案
func CreateDeployProposalFromCDS(chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte, policy []byte, escc []byte, vscc []byte) (*peer.Proposal, string, error) {
return createProposalFromCDS(chainID, cds, creator, policy, escc, vscc, "deploy")
}
//创建更新提案
func CreateUpgradeProposalFromCDS(chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte, policy []byte, escc []byte, vscc []byte) (*peer.Proposal, string, error) {
return createProposalFromCDS(chainID, cds, creator, policy, escc, vscc, "upgrade")
}
成功创建提案,根据签名所有人cf.Signer和提案prop进行签名。返回签名后的对象peer.SignedProposal
func GetSignedProposal(prop *peer.Proposal, signer msp.SigningIdentity) (*peer.SignedProposal, error) {
// check for nil argument
if prop == nil || signer == nil {
return nil, fmt.Errorf("Nil arguments")
}
propBytes, err := GetBytesProposal(prop)
if err != nil {
return nil, err
}
signature, err := signer.Sign(propBytes)
if err != nil {
return nil, err
}
return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, nil
}
当成功获取足够的消息后,对签名的提案在背书服务上进行处理。
背书服务对提案的处理过程移步hperledger fabric 1.0 源码分析之背书模拟交易过程查看
最后,当拿到提案响应结果proposalResponse后,将末签名的提案、签名所有人、提案响应组合并签名返回客户端,由客户端调用发送至orderder排序。组合方法详情如下:
func CreateSignedTx(proposal *peer.Proposal, signer msp.SigningIdentity, resps ...*peer.ProposalResponse) (*common.Envelope, error) {
if len(resps) == 0 {
return nil, fmt.Errorf("At least one proposal response is necessary")
}
// the original header
hdr, err := GetHeader(proposal.Header)
if err != nil {
return nil, fmt.Errorf("Could not unmarshal the proposal header")
}
// the original payload
pPayl, err := GetChaincodeProposalPayload(proposal.Payload)
if err != nil {
return nil, fmt.Errorf("Could not unmarshal the proposal payload")
}
// check that the signer is the same that is referenced in the header
// TODO: maybe worth removing?
signerBytes, err := signer.Serialize()
if err != nil {
return nil, err
}
shdr, err := GetSignatureHeader(hdr.SignatureHeader)
if err != nil {
return nil, err
}
if bytes.Compare(signerBytes, shdr.Creator) != 0 {
return nil, fmt.Errorf("The signer needs to be the same as the one referenced in the header")
}
// get header extensions so we have the visibility field
hdrExt, err := GetChaincodeHeaderExtension(hdr)
if err != nil {
return nil, err
}
// ensure that all actions are bitwise equal and that they are successful
var a1 []byte
for n, r := range resps {
if n == 0 {
a1 = r.Payload
if r.Response.Status != 200 {
return nil, fmt.Errorf("Proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message)
}
continue
}
if bytes.Compare(a1, r.Payload) != 0 {
return nil, fmt.Errorf("ProposalResponsePayloads do not match")
}
}
// fill endorsements
endorsements := make([]*peer.Endorsement, len(resps))
for n, r := range resps {
endorsements[n] = r.Endorsement
}
// create ChaincodeEndorsedAction
cea := &peer.ChaincodeEndorsedAction{ProposalResponsePayload: resps[0].Payload, Endorsements: endorsements}
// obtain the bytes of the proposal payload that will go to the transaction
propPayloadBytes, err := GetBytesProposalPayloadForTx(pPayl, hdrExt.PayloadVisibility)
if err != nil {
return nil, err
}
// serialize the chaincode action payload
cap := &peer.ChaincodeActionPayload{ChaincodeProposalPayload: propPayloadBytes, Action: cea}
capBytes, err := GetBytesChaincodeActionPayload(cap)
if err != nil {
return nil, err
}
// create a transaction
taa := &peer.TransactionAction{Header: hdr.SignatureHeader, Payload: capBytes}
taas := make([]*peer.TransactionAction, 1)
taas[0] = taa
tx := &peer.Transaction{Actions: taas}
// serialize the tx
txBytes, err := GetBytesTransaction(tx)
if err != nil {
return nil, err
}
// create the payload
payl := &common.Payload{Header: hdr, Data: txBytes}
paylBytes, err := GetBytesPayload(payl)
if err != nil {
return nil, err
}
// sign the payload
sig, err := signer.Sign(paylBytes)
if err != nil {
return nil, err
}
// here's the envelope
return &common.Envelope{Payload: paylBytes, Signature: sig}, nil
}