Skip to content

Commit e2bcb17

Browse files
committed
[FAB-2223] GetHistoryForKey Chaincode API
Expose GetHistoryForKey ledger API to chaincode. HistoryQueryExecutor utilizes a history database separate from state database, therefore it is made available outside the normal TxSimulator to minimize impact to state transactions (e.g. reduce lock times). HistoryQueryExecutor is made available to chaincode context, supporting GetHistoryForKey function calls from chaincode. Similar to GetQueryResult, existing chaincode iterator and Next support are re-used. marbles02 getHistoryForKey example is delivered. Call chaincode function as follows: peer chaincode invoke -C myc1 -n marbles -c '{"Args":["getHistoryForMarble","marble1"]}' Additional unit tests will be delivered next. Change-Id: Iee4dd92b27deb7c42cb83991fb6a3b924edd1e63 Signed-off-by: denyeart <[email protected]>
1 parent 5090331 commit e2bcb17

File tree

11 files changed

+415
-129
lines changed

11 files changed

+415
-129
lines changed

core/chaincode/chaincode_support.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ const (
5151

5252
//TXSimulatorKey is used to attach ledger simulation context
5353
TXSimulatorKey string = "txsimulatorkey"
54+
55+
//HistoryQueryExecutorKey is used to attach ledger history query executor context
56+
HistoryQueryExecutorKey string = "historyqueryexecutorkey"
5457
)
5558

5659
//this is basically the singleton that supports the
@@ -69,6 +72,15 @@ func getTxSimulator(context context.Context) ledger.TxSimulator {
6972
return nil
7073
}
7174

75+
//use this for ledger access and make sure HistoryQueryExecutor is being used
76+
func getHistoryQueryExecutor(context context.Context) ledger.HistoryQueryExecutor {
77+
if historyQueryExecutor, ok := context.Value(HistoryQueryExecutorKey).(ledger.HistoryQueryExecutor); ok {
78+
return historyQueryExecutor
79+
}
80+
//chaincode will not allow state operations
81+
return nil
82+
}
83+
7284
//
7385
//chaincode runtime environment encapsulates handler and container environment
7486
//This is where the VM that's running the chaincode would hook in
@@ -248,7 +260,7 @@ func (chaincodeSupport *ChaincodeSupport) registerHandler(chaincodehandler *Hand
248260

249261
func (chaincodeSupport *ChaincodeSupport) deregisterHandler(chaincodehandler *Handler) error {
250262

251-
// clean up rangeQueryIteratorMap
263+
// clean up queryIteratorMap
252264
for _, context := range chaincodehandler.txCtxs {
253265
for _, v := range context.queryIteratorMap {
254266
v.Close()

core/chaincode/handler.go

+133-15
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ type transactionContext struct {
6161
// tracks open iterators used for range queries
6262
queryIteratorMap map[string]commonledger.ResultsIterator
6363

64-
txsimulator ledger.TxSimulator
64+
txsimulator ledger.TxSimulator
65+
historyQueryExecutor ledger.HistoryQueryExecutor
6566
}
6667

6768
type nextStateInfo struct {
@@ -194,6 +195,7 @@ func (handler *Handler) createTxContext(ctxt context.Context, chainID string, tx
194195
queryIteratorMap: make(map[string]commonledger.ResultsIterator)}
195196
handler.txCtxs[txid] = txctx
196197
txctx.txsimulator = getTxSimulator(ctxt)
198+
txctx.historyQueryExecutor = getHistoryQueryExecutor(ctxt)
197199

198200
return txctx, nil
199201
}
@@ -417,6 +419,7 @@ func newChaincodeSupportHandler(chaincodeSupport *ChaincodeSupport, peerChatStre
417419
{Name: pb.ChaincodeMessage_GET_STATE.String(), Src: []string{readystate}, Dst: readystate},
418420
{Name: pb.ChaincodeMessage_GET_STATE_BY_RANGE.String(), Src: []string{readystate}, Dst: readystate},
419421
{Name: pb.ChaincodeMessage_GET_QUERY_RESULT.String(), Src: []string{readystate}, Dst: readystate},
422+
{Name: pb.ChaincodeMessage_GET_HISTORY_FOR_KEY.String(), Src: []string{readystate}, Dst: readystate},
420423
{Name: pb.ChaincodeMessage_QUERY_STATE_NEXT.String(), Src: []string{readystate}, Dst: readystate},
421424
{Name: pb.ChaincodeMessage_QUERY_STATE_CLOSE.String(), Src: []string{readystate}, Dst: readystate},
422425
{Name: pb.ChaincodeMessage_ERROR.String(), Src: []string{readystate}, Dst: readystate},
@@ -425,19 +428,20 @@ func newChaincodeSupportHandler(chaincodeSupport *ChaincodeSupport, peerChatStre
425428
{Name: pb.ChaincodeMessage_TRANSACTION.String(), Src: []string{readystate}, Dst: readystate},
426429
},
427430
fsm.Callbacks{
428-
"before_" + pb.ChaincodeMessage_REGISTER.String(): func(e *fsm.Event) { v.beforeRegisterEvent(e, v.FSM.Current()) },
429-
"before_" + pb.ChaincodeMessage_COMPLETED.String(): func(e *fsm.Event) { v.beforeCompletedEvent(e, v.FSM.Current()) },
430-
"after_" + pb.ChaincodeMessage_GET_STATE.String(): func(e *fsm.Event) { v.afterGetState(e, v.FSM.Current()) },
431-
"after_" + pb.ChaincodeMessage_GET_STATE_BY_RANGE.String(): func(e *fsm.Event) { v.afterGetStateByRange(e, v.FSM.Current()) },
432-
"after_" + pb.ChaincodeMessage_GET_QUERY_RESULT.String(): func(e *fsm.Event) { v.afterGetQueryResult(e, v.FSM.Current()) },
433-
"after_" + pb.ChaincodeMessage_QUERY_STATE_NEXT.String(): func(e *fsm.Event) { v.afterQueryStateNext(e, v.FSM.Current()) },
434-
"after_" + pb.ChaincodeMessage_QUERY_STATE_CLOSE.String(): func(e *fsm.Event) { v.afterQueryStateClose(e, v.FSM.Current()) },
435-
"after_" + pb.ChaincodeMessage_PUT_STATE.String(): func(e *fsm.Event) { v.enterBusyState(e, v.FSM.Current()) },
436-
"after_" + pb.ChaincodeMessage_DEL_STATE.String(): func(e *fsm.Event) { v.enterBusyState(e, v.FSM.Current()) },
437-
"after_" + pb.ChaincodeMessage_INVOKE_CHAINCODE.String(): func(e *fsm.Event) { v.enterBusyState(e, v.FSM.Current()) },
438-
"enter_" + establishedstate: func(e *fsm.Event) { v.enterEstablishedState(e, v.FSM.Current()) },
439-
"enter_" + readystate: func(e *fsm.Event) { v.enterReadyState(e, v.FSM.Current()) },
440-
"enter_" + endstate: func(e *fsm.Event) { v.enterEndState(e, v.FSM.Current()) },
431+
"before_" + pb.ChaincodeMessage_REGISTER.String(): func(e *fsm.Event) { v.beforeRegisterEvent(e, v.FSM.Current()) },
432+
"before_" + pb.ChaincodeMessage_COMPLETED.String(): func(e *fsm.Event) { v.beforeCompletedEvent(e, v.FSM.Current()) },
433+
"after_" + pb.ChaincodeMessage_GET_STATE.String(): func(e *fsm.Event) { v.afterGetState(e, v.FSM.Current()) },
434+
"after_" + pb.ChaincodeMessage_GET_STATE_BY_RANGE.String(): func(e *fsm.Event) { v.afterGetStateByRange(e, v.FSM.Current()) },
435+
"after_" + pb.ChaincodeMessage_GET_QUERY_RESULT.String(): func(e *fsm.Event) { v.afterGetQueryResult(e, v.FSM.Current()) },
436+
"after_" + pb.ChaincodeMessage_GET_HISTORY_FOR_KEY.String(): func(e *fsm.Event) { v.afterGetHistoryForKey(e, v.FSM.Current()) },
437+
"after_" + pb.ChaincodeMessage_QUERY_STATE_NEXT.String(): func(e *fsm.Event) { v.afterQueryStateNext(e, v.FSM.Current()) },
438+
"after_" + pb.ChaincodeMessage_QUERY_STATE_CLOSE.String(): func(e *fsm.Event) { v.afterQueryStateClose(e, v.FSM.Current()) },
439+
"after_" + pb.ChaincodeMessage_PUT_STATE.String(): func(e *fsm.Event) { v.enterBusyState(e, v.FSM.Current()) },
440+
"after_" + pb.ChaincodeMessage_DEL_STATE.String(): func(e *fsm.Event) { v.enterBusyState(e, v.FSM.Current()) },
441+
"after_" + pb.ChaincodeMessage_INVOKE_CHAINCODE.String(): func(e *fsm.Event) { v.enterBusyState(e, v.FSM.Current()) },
442+
"enter_" + establishedstate: func(e *fsm.Event) { v.enterEstablishedState(e, v.FSM.Current()) },
443+
"enter_" + readystate: func(e *fsm.Event) { v.enterReadyState(e, v.FSM.Current()) },
444+
"enter_" + endstate: func(e *fsm.Event) { v.enterEndState(e, v.FSM.Current()) },
441445
},
442446
)
443447

@@ -968,7 +972,7 @@ func (handler *Handler) handleGetQueryResult(msg *pb.ChaincodeMessage) {
968972
if err != nil {
969973
// Send error msg back to chaincode. GetState will not trigger event
970974
payload := []byte(err.Error())
971-
chaincodeLogger.Errorf("Failed to get ledger scan iterator. Sending %s", pb.ChaincodeMessage_ERROR)
975+
chaincodeLogger.Errorf("Failed to get ledger query iterator. Sending %s", pb.ChaincodeMessage_ERROR)
972976
serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid}
973977
return
974978
}
@@ -1017,6 +1021,118 @@ func (handler *Handler) handleGetQueryResult(msg *pb.ChaincodeMessage) {
10171021
}()
10181022
}
10191023

1024+
const maxGetHistoryForKeyLimit = 100
1025+
1026+
// afterGetHistoryForKey handles a GET_HISTORY_FOR_KEY request from the chaincode.
1027+
func (handler *Handler) afterGetHistoryForKey(e *fsm.Event, state string) {
1028+
msg, ok := e.Args[0].(*pb.ChaincodeMessage)
1029+
if !ok {
1030+
e.Cancel(fmt.Errorf("Received unexpected message type"))
1031+
return
1032+
}
1033+
chaincodeLogger.Debugf("Received %s, invoking get state from ledger", pb.ChaincodeMessage_GET_HISTORY_FOR_KEY)
1034+
1035+
// Query ledger history db
1036+
handler.handleGetHistoryForKey(msg)
1037+
chaincodeLogger.Debug("Exiting GET_HISTORY_FOR_KEY")
1038+
}
1039+
1040+
// Handles query to ledger history db
1041+
func (handler *Handler) handleGetHistoryForKey(msg *pb.ChaincodeMessage) {
1042+
// The defer followed by triggering a go routine dance is needed to ensure that the previous state transition
1043+
// is completed before the next one is triggered. The previous state transition is deemed complete only when
1044+
// the afterQueryState function is exited. Interesting bug fix!!
1045+
go func() {
1046+
// Check if this is the unique state request from this chaincode txid
1047+
uniqueReq := handler.createTXIDEntry(msg.Txid)
1048+
if !uniqueReq {
1049+
// Drop this request
1050+
chaincodeLogger.Error("Another state request pending for this Txid. Cannot process.")
1051+
return
1052+
}
1053+
1054+
var serialSendMsg *pb.ChaincodeMessage
1055+
1056+
defer func() {
1057+
handler.deleteTXIDEntry(msg.Txid)
1058+
chaincodeLogger.Debugf("[%s]handleGetHistoryForKey serial send %s", shorttxid(serialSendMsg.Txid), serialSendMsg.Type)
1059+
handler.serialSendAsync(serialSendMsg, nil)
1060+
}()
1061+
1062+
getHistoryForKey := &pb.GetHistoryForKey{}
1063+
unmarshalErr := proto.Unmarshal(msg.Payload, getHistoryForKey)
1064+
if unmarshalErr != nil {
1065+
payload := []byte(unmarshalErr.Error())
1066+
chaincodeLogger.Errorf("Failed to unmarshall query request. Sending %s", pb.ChaincodeMessage_ERROR)
1067+
serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid}
1068+
return
1069+
}
1070+
1071+
iterID := util.GenerateUUID()
1072+
1073+
var txContext *transactionContext
1074+
1075+
txContext, serialSendMsg = handler.isValidTxSim(msg.Txid, "[%s]No ledger context for GetHistoryForKey. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR)
1076+
if txContext == nil {
1077+
return
1078+
}
1079+
chaincodeID := handler.getCCRootName()
1080+
1081+
historyIter, err := txContext.historyQueryExecutor.GetHistoryForKey(chaincodeID, getHistoryForKey.Key)
1082+
if err != nil {
1083+
// Send error msg back to chaincode. GetState will not trigger event
1084+
payload := []byte(err.Error())
1085+
chaincodeLogger.Errorf("Failed to get ledger history iterator. Sending %s", pb.ChaincodeMessage_ERROR)
1086+
serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid}
1087+
return
1088+
}
1089+
1090+
handler.putQueryIterator(txContext, iterID, historyIter)
1091+
1092+
// TODO QueryStateKeyValue can be re-used for now since history records have a string (TxID)
1093+
// and value (value). But we'll need to use another structure if we add other fields like timestamp.
1094+
var keysAndValues []*pb.QueryStateKeyValue
1095+
var i = uint32(0)
1096+
var qresult commonledger.QueryResult
1097+
for ; i < maxGetHistoryForKeyLimit; i++ {
1098+
qresult, err = historyIter.Next()
1099+
if err != nil {
1100+
chaincodeLogger.Errorf("Failed to get query result from iterator. Sending %s", pb.ChaincodeMessage_ERROR)
1101+
return
1102+
}
1103+
if qresult == nil {
1104+
break
1105+
}
1106+
queryRecord := qresult.(*ledger.KeyModification)
1107+
keyAndValue := pb.QueryStateKeyValue{Key: queryRecord.TxID, Value: queryRecord.Value}
1108+
keysAndValues = append(keysAndValues, &keyAndValue)
1109+
}
1110+
1111+
if qresult != nil {
1112+
historyIter.Close()
1113+
handler.deleteQueryIterator(txContext, iterID)
1114+
}
1115+
1116+
var payloadBytes []byte
1117+
payload := &pb.QueryStateResponse{KeysAndValues: keysAndValues, HasMore: qresult != nil, Id: iterID}
1118+
payloadBytes, err = proto.Marshal(payload)
1119+
if err != nil {
1120+
historyIter.Close()
1121+
handler.deleteQueryIterator(txContext, iterID)
1122+
1123+
// Send error msg back to chaincode. GetState will not trigger event
1124+
payload := []byte(err.Error())
1125+
chaincodeLogger.Errorf("Failed marshall resopnse. Sending %s", pb.ChaincodeMessage_ERROR)
1126+
serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid}
1127+
return
1128+
}
1129+
1130+
chaincodeLogger.Debugf("Got keys and values. Sending %s", pb.ChaincodeMessage_RESPONSE)
1131+
serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: payloadBytes, Txid: msg.Txid}
1132+
1133+
}()
1134+
}
1135+
10201136
// afterPutState handles a PUT_STATE request from the chaincode.
10211137
func (handler *Handler) afterPutState(e *fsm.Event, state string) {
10221138
_, ok := e.Args[0].(*pb.ChaincodeMessage)
@@ -1141,6 +1257,7 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) {
11411257
// We grab the called channel's ledger simulator to hold the new state
11421258
ctxt := context.Background()
11431259
txsim := txContext.txsimulator
1260+
historyQueryExecutor := txContext.historyQueryExecutor
11441261
if calledCcParts.suffix != txContext.chainID {
11451262
lgr := peer.GetLedger(calledCcParts.suffix)
11461263
if lgr == nil {
@@ -1159,6 +1276,7 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) {
11591276
txsim = txsim2
11601277
}
11611278
ctxt = context.WithValue(ctxt, TXSimulatorKey, txsim)
1279+
ctxt = context.WithValue(ctxt, HistoryQueryExecutorKey, historyQueryExecutor)
11621280

11631281
if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
11641282
chaincodeLogger.Debugf("[%s] calling lccc to get chaincode data for %s on channel %s",

core/chaincode/shim/chaincode.go

+9
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,16 @@ func (stub *ChaincodeStub) GetQueryResult(query string) (StateQueryIteratorInter
357357
return nil, err
358358
}
359359
return &StateQueryIterator{stub.handler, stub.TxID, response, 0}, nil
360+
}
360361

362+
// GetHistoryForKey function can be invoked by a chaincode to return a history of
363+
// key values across time. GetHistoryForKey is intended to be used for read-only queries.
364+
func (stub *ChaincodeStub) GetHistoryForKey(key string) (StateQueryIteratorInterface, error) {
365+
response, err := stub.handler.handleGetHistoryForKey(key, stub.TxID)
366+
if err != nil {
367+
return nil, err
368+
}
369+
return &StateQueryIterator{stub.handler, stub.TxID, response, 0}, nil
361370
}
362371

363372
//CreateCompositeKey combines the given attributes to form a composite key.

core/chaincode/shim/handler.go

+48
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,54 @@ func (handler *Handler) handleGetQueryResult(query string, txid string) (*pb.Que
686686
return nil, errors.New("Incorrect chaincode message received")
687687
}
688688

689+
func (handler *Handler) handleGetHistoryForKey(key string, txid string) (*pb.QueryStateResponse, error) {
690+
// Create the channel on which to communicate the response from validating peer
691+
respChan, uniqueReqErr := handler.createChannel(txid)
692+
if uniqueReqErr != nil {
693+
chaincodeLogger.Debugf("[%s]Another state request pending for this Txid. Cannot process.", shorttxid(txid))
694+
return nil, uniqueReqErr
695+
}
696+
697+
defer handler.deleteChannel(txid)
698+
699+
// Send GET_HISTORY_FOR_KEY message to validator chaincode support
700+
payload := &pb.GetHistoryForKey{Key: key}
701+
payloadBytes, err := proto.Marshal(payload)
702+
if err != nil {
703+
return nil, errors.New("Failed to process query state request")
704+
}
705+
msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_HISTORY_FOR_KEY, Payload: payloadBytes, Txid: txid}
706+
chaincodeLogger.Debugf("[%s]Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_GET_HISTORY_FOR_KEY)
707+
responseMsg, err := handler.sendReceive(msg, respChan)
708+
if err != nil {
709+
chaincodeLogger.Errorf("[%s]error sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_GET_HISTORY_FOR_KEY)
710+
return nil, errors.New("could not send msg")
711+
}
712+
713+
if responseMsg.Type.String() == pb.ChaincodeMessage_RESPONSE.String() {
714+
// Success response
715+
chaincodeLogger.Debugf("[%s]Received %s. Successfully got range", shorttxid(responseMsg.Txid), pb.ChaincodeMessage_RESPONSE)
716+
717+
getHistoryForKeyResponse := &pb.QueryStateResponse{}
718+
unmarshalErr := proto.Unmarshal(responseMsg.Payload, getHistoryForKeyResponse)
719+
if unmarshalErr != nil {
720+
chaincodeLogger.Errorf("[%s]unmarshall error", shorttxid(responseMsg.Txid))
721+
return nil, errors.New("Error unmarshalling QueryStateResponse.")
722+
}
723+
724+
return getHistoryForKeyResponse, nil
725+
}
726+
if responseMsg.Type.String() == pb.ChaincodeMessage_ERROR.String() {
727+
// Error response
728+
chaincodeLogger.Errorf("[%s]Received %s", shorttxid(responseMsg.Txid), pb.ChaincodeMessage_ERROR)
729+
return nil, errors.New(string(responseMsg.Payload[:]))
730+
}
731+
732+
// Incorrect chaincode message received
733+
chaincodeLogger.Errorf("Incorrect chaincode message %s recieved. Expecting %s or %s", responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR)
734+
return nil, errors.New("Incorrect chaincode message received")
735+
}
736+
689737
// handleInvokeChaincode communicates with the validator to invoke another chaincode.
690738
func (handler *Handler) handleInvokeChaincode(chaincodeName string, args [][]byte, txid string) pb.Response {
691739
chaincodeID := &pb.ChaincodeID{Name: chaincodeName}

core/chaincode/shim/interfaces.go

+4
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ type ChaincodeStubInterface interface {
9797
// the query result set
9898
GetQueryResult(query string) (StateQueryIteratorInterface, error)
9999

100+
// GetHistoryForKey function can be invoked by a chaincode to return a history of
101+
// key values across time. GetHistoryForKey is intended to be used for read-only queries.
102+
GetHistoryForKey(key string) (StateQueryIteratorInterface, error)
103+
100104
// GetCallerCertificate returns caller certificate
101105
GetCallerCertificate() ([]byte, error)
102106

core/chaincode/shim/mockstub.go

+6
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,12 @@ func (stub *MockStub) GetQueryResult(query string) (StateQueryIteratorInterface,
204204
return nil, errors.New("Not Implemented")
205205
}
206206

207+
// GetHistoryForKey function can be invoked by a chaincode to return a history of
208+
// key values across time. GetHistoryForKey is intended to be used for read-only queries.
209+
func (stub *MockStub) GetHistoryForKey(key string) (StateQueryIteratorInterface, error) {
210+
return nil, errors.New("Not Implemented")
211+
}
212+
207213
//GetStateByPartialCompositeKey function can be invoked by a chaincode to query the
208214
//state based on a given partial composite key. This function returns an
209215
//iterator which can be used to iterate over all composite keys whose prefix

core/endorser/endorser.go

+18
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ func (*Endorser) getTxSimulator(ledgername string) (ledger.TxSimulator, error) {
7070
return lgr.NewTxSimulator()
7171
}
7272

73+
func (*Endorser) getHistoryQueryExecutor(ledgername string) (ledger.HistoryQueryExecutor, error) {
74+
lgr := peer.GetLedger(ledgername)
75+
if lgr == nil {
76+
return nil, fmt.Errorf("chain does not exist(%s)", ledgername)
77+
}
78+
return lgr.NewHistoryQueryExecutor()
79+
}
80+
7381
//call specified chaincode (system or user)
7482
func (e *Endorser) callChaincode(ctxt context.Context, chainID string, version string, txid string, prop *pb.Proposal, cis *pb.ChaincodeInvocationSpec, cid *pb.ChaincodeID, txsim ledger.TxSimulator) (*pb.Response, *pb.ChaincodeEvent, error) {
7583
var err error
@@ -298,11 +306,21 @@ func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedPro
298306

299307
// obtaining once the tx simulator for this proposal. This will be nil
300308
// for chainless proposals
309+
// Also obtain a history query executor for history queries, since tx simulator does not cover history
301310
var txsim ledger.TxSimulator
311+
var historyQueryExecutor ledger.HistoryQueryExecutor
302312
if chainID != "" {
303313
if txsim, err = e.getTxSimulator(chainID); err != nil {
304314
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
305315
}
316+
if historyQueryExecutor, err = e.getHistoryQueryExecutor(chainID); err != nil {
317+
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
318+
}
319+
// Add the historyQueryExecutor to context
320+
// TODO shouldn't we also add txsim to context here as well? Rather than passing txsim parameter
321+
// around separately, since eventually it gets added to context anyways
322+
ctx = context.WithValue(ctx, chaincode.HistoryQueryExecutorKey, historyQueryExecutor)
323+
306324
defer txsim.Done()
307325
}
308326
//this could be a request to a chainless SysCC

0 commit comments

Comments
 (0)