sample_chain/proto/util/txutils.go

493 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package util
import (
"bytes"
"crypto/sha256"
pb "schain/proto"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"
)
// GetChaincodeActionPayloadFromTransaction 从Transaction中获取ChaincodeActionPayload
func GetChaincodeActionPayloadFromTransaction(tx *pb.Transaction) (*pb.ChaincodeActionPayload, error) {
if tx.Payload == nil {
return nil, errors.New("no ChaincodeActionPayload in Transaction")
}
ccPayload := tx.Payload
return ccPayload, nil
}
// GetProposalResponsePayloadFromTransaction 从Transaction中获取ProposalResponsePayload
func GetProposalResponsePayloadFromTransaction(tx *pb.Transaction) (*pb.ProposalResponsePayload, error) {
ccPayload, err := GetChaincodeActionPayloadFromTransaction(tx)
if err != nil {
return nil, errors.New("no ChaincodeActionPayload in Transaction")
}
if ccPayload.ProposalResponsePayload == nil {
return nil, errors.New("no ProposalResponsePayload in ChaincodeActionPayload")
}
prpBytes := ccPayload.ProposalResponsePayload
prp, err := UnmarshalProposalResponsePayload(prpBytes)
if err != nil {
return nil, errors.New("Unmarshal ProposalResponsePayload error")
}
return prp, nil
}
// CreateSignedEnvelope 新建一个附带签名的Envelope
func CreateSignedEnvelope(
signer Signer,
dataMsg proto.Message,
) (*pb.Envelope, error) {
var err error
payloadSignatureHeader := &pb.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(
&pb.Payload{
SignatureHeader: payloadSignatureHeader,
Data: data,
},
)
var sig []byte
if signer != nil {
sig, err = signer.Sign(paylBytes)
if err != nil {
return nil, err
}
}
env := &pb.Envelope{
Payload: paylBytes,
Signature: sig,
}
return env, nil
}
type Signer interface {
Sign(msg []byte) ([]byte, error)
Serialize() ([]byte, error)
}
// CreateSignedTx 创建一笔附带签名的交易
func CreateSignedTx(
txID string,
proposal *pb.Proposal,
signer Signer,
resps ...*pb.ProposalResponse,
) (*pb.Envelope, error) {
if len(resps) == 0 {
return nil, errors.New("at least one proposal response is required")
}
if signer == nil {
return nil, errors.New("signer is required when creating a signed transaction")
}
// the original header
shdr, err := UnmarshalSignatureHeader(proposal.Header)
if err != nil {
return nil, err
}
// the original payload
cs, err := UnmarshalChaincodeSpec(proposal.Payload)
if err != nil {
return nil, err
}
// check that the signer is the same that is referenced in the header
signerBytes, err := signer.Serialize()
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.Errorf("Endorsement results are inconsistent")
}
}
// fill endorsements according to their uniqueness
endorsersUsed := make(map[string]struct{})
var endorsements []*pb.Endorsement
for _, r := range resps {
if r.Endorsement == nil {
continue
}
key := string(r.Endorsement.Endorser)
if _, used := endorsersUsed[key]; used {
continue
}
endorsements = append(endorsements, r.Endorsement)
endorsersUsed[key] = struct{}{}
}
if len(endorsements) == 0 {
return nil, errors.Errorf("no endorsements")
}
// create a transaction
caPayload := &pb.ChaincodeActionPayload{Input: cs, Endorsements: endorsements, ProposalResponsePayload: resps[0].Payload}
tx := &pb.Transaction{Payload: caPayload}
// serialize the tx
txBytes, err := GetBytesTransaction(tx)
if err != nil {
return nil, err
}
// create the payload
payl := &pb.Payload{SignatureHeader: shdr, 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 &pb.Envelope{TxID: txID, Payload: paylBytes, Signature: sig}, nil
}
// CreateProposalResponse 新建一个提案回复ProposalResponse
func CreateProposalResponse(
shdrbytes []byte,
payload []byte,
response *pb.Response,
results []byte,
signingEndorser Signer,
) (*pb.ProposalResponse, error) {
shdr, err := UnmarshalSignatureHeader(shdrbytes)
if err != nil {
return nil, err
}
pHashBytes, err := GetProposalHash(shdr, payload)
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)
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 := &pb.ProposalResponse{
Version: 1,
Timestamp: timestamppb.Now(),
Endorsement: &pb.Endorsement{
Signature: signature,
Endorser: endorser,
},
Payload: prpBytes,
Response: &pb.Response{
Status: 200,
Message: "OK",
},
}
return resp, nil
}
// CreateProposalResponseFailure 当背书提案失败或者链码失败时,创建一个 ProposalResponse
func CreateProposalResponseFailure(
hdrbytes []byte,
payl []byte,
response *pb.Response,
results []byte,
) (*pb.ProposalResponse, error) {
shdr, err := UnmarshalSignatureHeader(hdrbytes)
if err != nil {
return nil, err
}
// obtain the proposal hash given proposal header, payload and the requested visibility
pHashBytes, err := GetProposalHash(shdr, 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)
if err != nil {
return nil, err
}
resp := &pb.ProposalResponse{
// Timestamp: TODO!
Payload: prpBytes,
Response: response,
}
return resp, nil
}
// GetBytesProposalPayloadForTx 从交易中获取ProposalPayload的字节数组
func GetBytesProposalPayloadForTx(
payload *pb.ChaincodeSpec,
) ([]byte, error) {
// check for nil argument
if payload == nil {
return nil, errors.New("nil arguments")
}
// strip the transient bytes off the payload
cppNoTransient := &pb.ChaincodeSpec{Input: payload.Input}
cppBytes, err := GetBytesChaincodeSpec(cppNoTransient)
if err != nil {
return nil, err
}
return cppBytes, nil
}
// GetProposalHash 获取Proposal的hash
func GetProposalHash(signatureHeader *pb.SignatureHeader, ccPropPayl []byte) ([]byte, error) {
// check for nil argument
if signatureHeader == nil ||
ccPropPayl == nil {
return nil, errors.New("nil arguments")
}
// unmarshal the chaincode proposal payload
cs, err := UnmarshalChaincodeSpec(ccPropPayl)
if err != nil {
return nil, err
}
ppBytes, err := GetBytesProposalPayloadForTx(cs)
if err != nil {
return nil, err
}
signatureHeaderData, err := proto.Marshal(signatureHeader)
if err != nil {
return nil, err
}
hash2 := sha256.New()
// hash the serialized Signature Header object
hash2.Write(signatureHeaderData)
// hash of the part of the chaincode proposal payload that will go to the tx
hash2.Write(ppBytes)
return hash2.Sum(nil), nil
}
// GetSignedProposal 获取SignedProposal
func GetSignedProposal(proposal *pb.Proposal, signer Signer) (*pb.SignedProposal, error) {
// check for nil argument
if proposal == nil {
return nil, errors.New("proposal cannot be nil")
}
if signer == nil {
return nil, errors.New("signer cannot be nil")
}
proposalBytes, err := proto.Marshal(proposal)
if err != nil {
return nil, errors.Wrap(err, "error marshaling proposal")
}
signature, err := signer.Sign(proposalBytes)
if err != nil {
return nil, err
}
return &pb.SignedProposal{
ProposalBytes: proposalBytes,
Signature: signature,
}, nil
}
func GetEndorserMsgFromEnvelope(envelop *pb.Envelope) ([]byte, error) {
tx, err := GetTransactionFromEnvelope(envelop)
if err != nil {
return nil, errors.WithMessage(err, "error getting tx from envelop")
}
csBytes, err := GetBytesChaincodeSpec(tx.Payload.Input)
if err != nil {
return nil, errors.WithMessage(err, "error getting chaincodeSpecBytes from tx")
}
payload, err := UnmarshalPayload(envelop.Payload)
if err != nil {
return nil, errors.WithMessage(err, "error getting txPayload from envelop payload")
}
pHashBytes, err := GetProposalHash(payload.SignatureHeader, csBytes)
if err != nil {
return nil, errors.WithMessage(err, "error getting proposalHash")
}
ca, err := GetChaincodeActionFromEnvelope(envelop)
if err != nil {
return nil, errors.WithMessage(err, "error getting chaincodeAction from envelop")
}
prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, ca.Response, ca.Results)
if err != nil {
return nil, errors.WithMessage(err, "error getting txPayload from envelop")
}
return prpBytes, nil
}
func GetPayloadFromEnvelope(envelop *pb.Envelope) (*pb.Payload, error) {
txPayload, err := UnmarshalPayload(envelop.Payload)
if err != nil {
return nil, errors.WithMessage(err, "error getting txPayload from envelop")
}
return txPayload, nil
}
func GetTransactionFromEnvelope(envelop *pb.Envelope) (*pb.Transaction, error) {
txPayload, err := GetPayloadFromEnvelope(envelop)
if err != nil {
return nil, errors.WithMessage(err, "error getting txPayload from envelop")
}
if txPayload.Data == nil {
return nil, errors.New("error getting txPayload data: payload data is nil")
}
tx, err := UnmarshalTransaction(txPayload.Data)
if err != nil {
return nil, errors.WithMessage(err, "error getting transaction from envelop")
}
return tx, nil
}
func GetProposalResponsePayloadFromEnvelope(envelop *pb.Envelope) (*pb.ProposalResponsePayload, error) {
tx, err := GetTransactionFromEnvelope(envelop)
if err != nil {
return nil, errors.WithMessage(err, "error getting tx from envelop")
}
if tx.Payload == nil {
return nil, errors.New("error getting tx Payload : tx Payload is nil")
}
proposalResponsePayload, err := UnmarshalProposalResponsePayload(tx.Payload.ProposalResponsePayload)
if err != nil {
return nil, errors.WithMessage(err, "error getting proposalResponsePayload from envelop")
}
return proposalResponsePayload, nil
}
func GetChaincodeActionFromEnvelope(envelope *pb.Envelope) (*pb.ChaincodeAction, error) {
proposalResponsePayload, err := GetProposalResponsePayloadFromEnvelope(envelope)
if err != nil {
return nil, errors.WithMessage(err, "error getting proposalResponsePayload from envelop")
}
if proposalResponsePayload.Extension == nil {
return nil, errors.New("error getting proposalResponsePayload Extension: proposalResponsePayload Extension is nil")
}
ca, err := UnmarshalChaincodeAction(proposalResponsePayload.Extension)
if err != nil {
return nil, errors.WithMessage(err, "error getting chaincodeAction from envelop")
}
return ca, nil
}
func GetKVRWSetKwFromEnvelope(envelop *pb.Envelope) (*pb.KVRWSet, error) {
ca, err := GetChaincodeActionFromEnvelope(envelop)
if err != nil {
return nil, errors.WithMessage(err, "error getting ChaincodeAction from Envelope")
}
kVRWSet, err := UnmarshalKVRWSet(ca.Results)
if err != nil {
return nil, errors.WithMessage(err, "error getting kVRWSet ChaincodeAction")
}
return kVRWSet, nil
}
func GetOrComputeTxIDFromEnvelope(envelop *pb.Envelope) (string, error) {
if envelop.TxID != "" {
return envelop.TxID, nil
}
txPayload, err := UnmarshalPayload(envelop.Payload)
if err != nil {
return "", errors.WithMessage(err, "error getting txID from payload")
}
if txPayload.SignatureHeader == nil {
return "", errors.New("error getting txID from signatureHeader: payload signatureHeader is nil")
}
sighdr := txPayload.SignatureHeader
txID := ComputeTxID(sighdr.Nonce, sighdr.Creator)
return txID, nil
}
// GetOrComputeTxIDFromEnvelopeBytes 从Envelope中获取交易ID或者计算交易ID
func GetOrComputeTxIDFromEnvelopeBytes(txEnvelopBytes []byte) (string, error) {
txEnvelope, err := UnmarshalEnvelope(txEnvelopBytes)
if err != nil {
return "", errors.WithMessage(err, "error getting txID from envelope")
}
return GetOrComputeTxIDFromEnvelope(txEnvelope)
}