1 Star 0 Fork 0

13683679291/fabric

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
txutils.go 13.77 KB
一键复制 编辑 原始数据 按行查看 历史
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package protoutil
import (
"bytes"
"crypto/sha256"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric-protos-go/peer"
"github.com/pkg/errors"
)
// GetPayloads gets the underlying payload objects in a TransactionAction
func GetPayloads(txActions *peer.TransactionAction) (*peer.ChaincodeActionPayload, *peer.ChaincodeAction, error) {
// TODO: pass in the tx type (in what follows we're assuming the
// type is ENDORSER_TRANSACTION)
ccPayload, err := UnmarshalChaincodeActionPayload(txActions.Payload)
if err != nil {
return nil, nil, err
}
if ccPayload.Action == nil || ccPayload.Action.ProposalResponsePayload == nil {
return nil, nil, errors.New("no payload in ChaincodeActionPayload")
}
pRespPayload, err := UnmarshalProposalResponsePayload(ccPayload.Action.ProposalResponsePayload)
if err != nil {
return nil, nil, err
}
if pRespPayload.Extension == nil {
return nil, nil, errors.New("response payload is missing extension")
}
respPayload, err := UnmarshalChaincodeAction(pRespPayload.Extension)
if err != nil {
return ccPayload, nil, err
}
return ccPayload, respPayload, nil
}
// GetEnvelopeFromBlock gets an envelope from a block's Data field.
func GetEnvelopeFromBlock(data []byte) (*common.Envelope, error) {
// Block always begins with an envelope
var err error
env := &common.Envelope{}
if err = proto.Unmarshal(data, env); err != nil {
return nil, errors.Wrap(err, "error unmarshaling Envelope")
}
return env, nil
}
// CreateSignedEnvelope creates a signed envelope of the desired type, with
// marshaled dataMsg and signs it
func CreateSignedEnvelope(
txType common.HeaderType,
channelID string,
signer Signer,
dataMsg proto.Message,
msgVersion int32,
epoch uint64,
) (*common.Envelope, error) {
return CreateSignedEnvelopeWithTLSBinding(txType, channelID, signer, dataMsg, msgVersion, epoch, nil)
}
// CreateSignedEnvelopeWithTLSBinding creates a signed envelope of the desired
// type, with marshaled dataMsg and signs it. It also includes a TLS cert hash
// into the channel header
func CreateSignedEnvelopeWithTLSBinding(
txType common.HeaderType,
channelID string,
signer Signer,
dataMsg proto.Message,
msgVersion int32,
epoch uint64,
tlsCertHash []byte,
) (*common.Envelope, error) {
payloadChannelHeader := MakeChannelHeader(txType, msgVersion, channelID, epoch)
payloadChannelHeader.TlsCertHash = tlsCertHash
var err error
payloadSignatureHeader := &common.SignatureHeader{}
if signer != nil {
payloadSignatureHeader, err = NewSignatureHeader(signer)
if err != nil {
return nil, err
}
}
data, err := proto.Marshal(dataMsg)
if err != nil {
return nil, errors.Wrap(err, "error marshaling")
}
paylBytes := MarshalOrPanic(
&common.Payload{
Header: MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader),
Data: data,
},
)
var sig []byte
if signer != nil {
sig, err = signer.Sign(paylBytes)
if err != nil {
return nil, err
}
}
env := &common.Envelope{
Payload: paylBytes,
Signature: sig,
}
return env, nil
}
// Signer is the interface needed to sign a transaction
type Signer interface {
Sign(msg []byte) ([]byte, error)
Serialize() ([]byte, error)
}
// CreateSignedTx assembles an Envelope message from proposal, endorsements,
// and a signer. This function should be called by a client when it has
// collected enough endorsements for a proposal to create a transaction and
// submit it to peers for ordering
func CreateSignedTx(
proposal *peer.Proposal,
signer Signer,
resps ...*peer.ProposalResponse,
) (*common.Envelope, error) {
if len(resps) == 0 {
return nil, errors.New("at least one proposal response is required")
}
// the original header
hdr, err := UnmarshalHeader(proposal.Header)
if err != nil {
return nil, err
}
// the original payload
pPayl, err := UnmarshalChaincodeProposalPayload(proposal.Payload)
if err != nil {
return nil, err
}
// 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 := UnmarshalSignatureHeader(hdr.SignatureHeader)
if err != nil {
return nil, err
}
if !bytes.Equal(signerBytes, shdr.Creator) {
return nil, errors.New("signer must be the same as the one referenced in the header")
}
// ensure that all actions are bitwise equal and that they are successful
var a1 []byte
for n, r := range resps {
if r.Response.Status < 200 || r.Response.Status >= 400 {
return nil, errors.Errorf("proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message)
}
if n == 0 {
a1 = r.Payload
continue
}
if !bytes.Equal(a1, r.Payload) {
return nil, errors.New("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)
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
}
// CreateProposalResponse creates a proposal response.
func CreateProposalResponse(
hdrbytes []byte,
payl []byte,
response *peer.Response,
results []byte,
events []byte,
ccid *peer.ChaincodeID,
signingEndorser Signer,
) (*peer.ProposalResponse, error) {
hdr, err := UnmarshalHeader(hdrbytes)
if err != nil {
return nil, err
}
// obtain the proposal hash given proposal header, payload and the
// requested visibility
pHashBytes, err := GetProposalHash1(hdr, payl)
if err != nil {
return nil, errors.WithMessage(err, "error computing proposal hash")
}
// get the bytes of the proposal response payload - we need to sign them
prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, ccid)
if err != nil {
return nil, err
}
// serialize the signing identity
endorser, err := signingEndorser.Serialize()
if err != nil {
return nil, errors.WithMessage(err, "error serializing signing identity")
}
// sign the concatenation of the proposal response and the serialized
// endorser identity with this endorser's key
signature, err := signingEndorser.Sign(append(prpBytes, endorser...))
if err != nil {
return nil, errors.WithMessage(err, "could not sign the proposal response payload")
}
resp := &peer.ProposalResponse{
// Timestamp: TODO!
Version: 1, // TODO: pick right version number
Endorsement: &peer.Endorsement{
Signature: signature,
Endorser: endorser,
},
Payload: prpBytes,
Response: &peer.Response{
Status: 200,
Message: "OK",
},
}
return resp, nil
}
// CreateProposalResponseFailure creates a proposal response for cases where
// endorsement proposal fails either due to a endorsement failure or a
// chaincode failure (chaincode response status >= shim.ERRORTHRESHOLD)
func CreateProposalResponseFailure(
hdrbytes []byte,
payl []byte,
response *peer.Response,
results []byte,
events []byte,
chaincodeName string,
) (*peer.ProposalResponse, error) {
hdr, err := UnmarshalHeader(hdrbytes)
if err != nil {
return nil, err
}
// obtain the proposal hash given proposal header, payload and the requested visibility
pHashBytes, err := GetProposalHash1(hdr, payl)
if err != nil {
return nil, errors.WithMessage(err, "error computing proposal hash")
}
// get the bytes of the proposal response payload
prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, &peer.ChaincodeID{Name: chaincodeName})
if err != nil {
return nil, err
}
resp := &peer.ProposalResponse{
// Timestamp: TODO!
Payload: prpBytes,
Response: response,
}
return resp, nil
}
// GetSignedProposal returns a signed proposal given a Proposal message and a
// signing identity
func GetSignedProposal(prop *peer.Proposal, signer Signer) (*peer.SignedProposal, error) {
// check for nil argument
if prop == nil || signer == nil {
return nil, errors.New("nil arguments")
}
propBytes, err := proto.Marshal(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
}
// MockSignedEndorserProposalOrPanic creates a SignedProposal with the
// passed arguments
func MockSignedEndorserProposalOrPanic(
channelID string,
cs *peer.ChaincodeSpec,
creator,
signature []byte,
) (*peer.SignedProposal, *peer.Proposal) {
prop, _, err := CreateChaincodeProposal(
common.HeaderType_ENDORSER_TRANSACTION,
channelID,
&peer.ChaincodeInvocationSpec{ChaincodeSpec: cs},
creator)
if err != nil {
panic(err)
}
propBytes, err := proto.Marshal(prop)
if err != nil {
panic(err)
}
return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, prop
}
func MockSignedEndorserProposal2OrPanic(
channelID string,
cs *peer.ChaincodeSpec,
signer Signer,
) (*peer.SignedProposal, *peer.Proposal) {
serializedSigner, err := signer.Serialize()
if err != nil {
panic(err)
}
prop, _, err := CreateChaincodeProposal(
common.HeaderType_ENDORSER_TRANSACTION,
channelID,
&peer.ChaincodeInvocationSpec{ChaincodeSpec: &peer.ChaincodeSpec{}},
serializedSigner)
if err != nil {
panic(err)
}
sProp, err := GetSignedProposal(prop, signer)
if err != nil {
panic(err)
}
return sProp, prop
}
// GetBytesProposalPayloadForTx takes a ChaincodeProposalPayload and returns
// its serialized version according to the visibility field
func GetBytesProposalPayloadForTx(
payload *peer.ChaincodeProposalPayload,
) ([]byte, error) {
// check for nil argument
if payload == nil {
return nil, errors.New("nil arguments")
}
// strip the transient bytes off the payload
cppNoTransient := &peer.ChaincodeProposalPayload{Input: payload.Input, TransientMap: nil}
cppBytes, err := GetBytesChaincodeProposalPayload(cppNoTransient)
if err != nil {
return nil, err
}
return cppBytes, nil
}
// GetProposalHash2 gets the proposal hash - this version
// is called by the committer where the visibility policy
// has already been enforced and so we already get what
// we have to get in ccPropPayl
func GetProposalHash2(header *common.Header, ccPropPayl []byte) ([]byte, error) {
// check for nil argument
if header == nil ||
header.ChannelHeader == nil ||
header.SignatureHeader == nil ||
ccPropPayl == nil {
return nil, errors.New("nil arguments")
}
hash := sha256.New()
// hash the serialized Channel Header object
hash.Write(header.ChannelHeader)
// hash the serialized Signature Header object
hash.Write(header.SignatureHeader)
// hash the bytes of the chaincode proposal payload that we are given
hash.Write(ccPropPayl)
return hash.Sum(nil), nil
}
// GetProposalHash1 gets the proposal hash bytes after sanitizing the
// chaincode proposal payload according to the rules of visibility
func GetProposalHash1(header *common.Header, ccPropPayl []byte) ([]byte, error) {
// check for nil argument
if header == nil ||
header.ChannelHeader == nil ||
header.SignatureHeader == nil ||
ccPropPayl == nil {
return nil, errors.New("nil arguments")
}
// unmarshal the chaincode proposal payload
cpp, err := UnmarshalChaincodeProposalPayload(ccPropPayl)
if err != nil {
return nil, err
}
ppBytes, err := GetBytesProposalPayloadForTx(cpp)
if err != nil {
return nil, err
}
hash2 := sha256.New()
// hash the serialized Channel Header object
hash2.Write(header.ChannelHeader)
// hash the serialized Signature Header object
hash2.Write(header.SignatureHeader)
// hash of the part of the chaincode proposal payload that will go to the tx
hash2.Write(ppBytes)
return hash2.Sum(nil), nil
}
// GetOrComputeTxIDFromEnvelope gets the txID present in a given transaction
// envelope. If the txID is empty, it constructs the txID from nonce and
// creator fields in the envelope.
func GetOrComputeTxIDFromEnvelope(txEnvelopBytes []byte) (string, error) {
txEnvelope, err := UnmarshalEnvelope(txEnvelopBytes)
if err != nil {
return "", errors.WithMessage(err, "error getting txID from envelope")
}
txPayload, err := UnmarshalPayload(txEnvelope.Payload)
if err != nil {
return "", errors.WithMessage(err, "error getting txID from payload")
}
if txPayload.Header == nil {
return "", errors.New("error getting txID from header: payload header is nil")
}
chdr, err := UnmarshalChannelHeader(txPayload.Header.ChannelHeader)
if err != nil {
return "", errors.WithMessage(err, "error getting txID from channel header")
}
if chdr.TxId != "" {
return chdr.TxId, nil
}
sighdr, err := UnmarshalSignatureHeader(txPayload.Header.SignatureHeader)
if err != nil {
return "", errors.WithMessage(err, "error getting nonce and creator for computing txID")
}
txid := ComputeTxID(sighdr.Nonce, sighdr.Creator)
return txid, nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mmcro/fabric.git
git@gitee.com:mmcro/fabric.git
mmcro
fabric
fabric
v2.1.1

搜索帮助