forked from hyperledger/fabric-sdk-go
/
proposal.go
163 lines (131 loc) · 5.01 KB
/
proposal.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package txn
import (
reqContext "context"
"sync"
"github.com/golang/protobuf/proto"
"github.com/pkg/errors"
"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/multi"
contextApi "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
"github.com/hyperledger/fabric-sdk-go/pkg/context"
"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer"
protos_utils "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/utils"
)
// CreateChaincodeInvokeProposal creates a proposal for transaction.
func CreateChaincodeInvokeProposal(txh fab.TransactionHeader, request fab.ChaincodeInvokeRequest) (*fab.TransactionProposal, error) {
if request.ChaincodeID == "" {
return nil, errors.New("ChaincodeID is required")
}
if request.Fcn == "" {
return nil, errors.New("Fcn is required")
}
// Add function name to arguments
argsArray := make([][]byte, len(request.Args)+1)
argsArray[0] = []byte(request.Fcn)
for i, arg := range request.Args {
argsArray[i+1] = arg
}
// create invocation spec to target a chaincode with arguments
ccis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{
Type: pb.ChaincodeSpec_GOLANG, ChaincodeId: &pb.ChaincodeID{Name: request.ChaincodeID},
Input: &pb.ChaincodeInput{Args: argsArray}}}
proposal, _, err := protos_utils.CreateChaincodeProposalWithTxIDNonceAndTransient(string(txh.TransactionID()), common.HeaderType_ENDORSER_TRANSACTION, txh.ChannelID(), ccis, txh.Nonce(), txh.Creator(), request.TransientMap)
if err != nil {
return nil, errors.Wrap(err, "failed to create chaincode proposal")
}
tp := fab.TransactionProposal{
TxnID: txh.TransactionID(),
Proposal: proposal,
}
return &tp, nil
}
// signProposal creates a SignedProposal based on the current context.
func signProposal(ctx contextApi.Client, proposal *pb.Proposal) (*pb.SignedProposal, error) {
proposalBytes, err := proto.Marshal(proposal)
if err != nil {
return nil, errors.Wrap(err, "mashal proposal failed")
}
signingMgr := ctx.SigningManager()
if signingMgr == nil {
return nil, errors.New("signing manager is nil")
}
signature, err := signingMgr.Sign(proposalBytes, ctx.PrivateKey())
if err != nil {
return nil, errors.WithMessage(err, "sign failed")
}
return &pb.SignedProposal{ProposalBytes: proposalBytes, Signature: signature}, nil
}
// SendProposal sends a TransactionProposal to ProposalProcessor.
func SendProposal(reqCtx reqContext.Context, proposal *fab.TransactionProposal, targets []fab.ProposalProcessor) ([]*fab.TransactionProposalResponse, error) {
if proposal == nil {
return nil, errors.New("proposal is required")
}
if len(targets) < 1 {
return nil, errors.New("targets is required")
}
for _, p := range targets {
if p == nil {
return nil, errors.New("target is nil")
}
}
targets = getTargetsWithoutDuplicates(targets)
ctx, ok := context.RequestClientContext(reqCtx)
if !ok {
return nil, errors.New("failed get client context from reqContext for signProposal")
}
signedProposal, err := signProposal(ctx, proposal.Proposal)
if err != nil {
return nil, errors.WithMessage(err, "sign proposal failed")
}
request := fab.ProcessProposalRequest{SignedProposal: signedProposal}
var responseMtx sync.Mutex
var transactionProposalResponses []*fab.TransactionProposalResponse
var wg sync.WaitGroup
errs := multi.Errors{}
for _, p := range targets {
wg.Add(1)
go func(processor fab.ProposalProcessor) {
defer wg.Done()
// TODO: The RPC should be timed-out.
//resp, err := processor.ProcessTransactionProposal(context.NewRequestOLD(ctx), request)
resp, err := processor.ProcessTransactionProposal(reqCtx, request)
if err != nil {
logger.Debugf("Received error response from txn proposal processing: %s", err)
responseMtx.Lock()
errs = append(errs, err)
responseMtx.Unlock()
return
}
responseMtx.Lock()
transactionProposalResponses = append(transactionProposalResponses, resp)
responseMtx.Unlock()
}(p)
}
wg.Wait()
return transactionProposalResponses, errs.ToError()
}
// getTargetsWithoutDuplicates returns a list of targets without duplicates
func getTargetsWithoutDuplicates(targets []fab.ProposalProcessor) []fab.ProposalProcessor {
peerUrlsToTargets := map[string]fab.ProposalProcessor{}
var uniqueTargets []fab.ProposalProcessor
for i := range targets {
peer, ok := targets[i].(fab.Peer)
if !ok {
// ProposalProcessor is not a fab.Peer... cannot remove duplicates
return targets
}
if _, present := peerUrlsToTargets[peer.URL()]; !present {
uniqueTargets = append(uniqueTargets, targets[i])
peerUrlsToTargets[peer.URL()] = targets[i]
}
}
if len(uniqueTargets) != len(targets) {
logger.Warn("Duplicate target peers in configuration")
}
return uniqueTargets
}