493 lines
13 KiB
Go
493 lines
13 KiB
Go
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)
|
||
}
|