From b2f9d56aaf944d49310079006bbb13a2ae0ff19b Mon Sep 17 00:00:00 2001 From: senthil Date: Wed, 15 Mar 2017 19:04:19 +0530 Subject: [PATCH] FAB-2055 GetHistoryForKey() returns timestamp Updated Ledger's GetHistoryForKey() API to return key, value, timestamp and delete marker. Further, query response from ledger is directly forwarded to shim layer to cast the response appropriately and return to chaincode as a struct. We have made the following changes to realize this. (i) Renamed QueryStateResponse proto message to QueryResponse. The QueryResult from ledger is converted to a 2D byte array (encoding using gob) and appropriately set in QueryResponse struct. Hence, the QueryResponse is consistent across different query types. This approach reduces the repetition of code in peer's handler.go that process the QueryResult from ledger. (iii) Introduced two types of iterator in chaincode shim: One for iterating over QueryResult of range/execute query and another for iterating over QueryResult of history query. Now, these iterator processes QueryResult (i.e., 2D byte array, decoded using gob) and returns a struct to chaincode: - ledger.KV struct for range and execute query, - ledger.KeyModification struct for history query. As a result, chaincode examples (map.go, marbles_chaincode.go) and mockstub have been modified (iv) Added test for historyQueries (from shim layer) (v) As java shim is significnatly behind go shim, I have commented out range query functionality for now. As a separate changeset, need to update java shim's range query with updated chaincode proto and add other query functionalities. Change-Id: I5e0cfd5f712f686361f8610d69db57646ad52b4f Signed-off-by: senthil --- core/chaincode/exectransaction_test.go | 58 +++++ core/chaincode/handler.go | 203 ++++++++---------- core/chaincode/shim/chaincode.go | 113 +++++++--- core/chaincode/shim/handler.go | 28 +-- core/chaincode/shim/interfaces.go | 41 +++- .../hyperledger/java/shim/ChaincodeStub.java | 12 +- .../org/hyperledger/java/shim/Handler.java | 4 +- core/chaincode/shim/mockstub.go | 13 +- core/chaincode/shim/mockstub_test.go | 30 +-- .../historyleveldb_query_executer.go | 31 +-- .../historyleveldb/historyleveldb_test.go | 31 ++- .../txmgmt/statedb/commontests/test_common.go | 44 ++-- .../statedb/statecouchdb/statecouchdb.go | 8 +- .../ledger/kvledger/txmgmt/statedb/statedb.go | 8 - .../txmgmt/txmgr/commontests/txmgr_test.go | 2 +- .../txmgmt/txmgr/lockbasedtxmgr/helper.go | 6 +- core/ledger/ledger_interface.go | 15 +- core/scc/lccc/lccc.go | 4 +- examples/chaincode/go/map/map.go | 39 +++- .../go/marbles02/marbles_chaincode.go | 42 ++-- protos/peer/chaincode_shim.pb.go | 136 ++++++------ protos/peer/chaincode_shim.proto | 9 +- 22 files changed, 530 insertions(+), 347 deletions(-) diff --git a/core/chaincode/exectransaction_test.go b/core/chaincode/exectransaction_test.go index fb639187695..91ec368fc8a 100644 --- a/core/chaincode/exectransaction_test.go +++ b/core/chaincode/exectransaction_test.go @@ -138,8 +138,13 @@ func startTxSimulation(ctxt context.Context, chainID string) (context.Context, l if err != nil { return nil, nil, err } + historyQueryExecutor, err := lgr.NewHistoryQueryExecutor() + if err != nil { + return nil, nil, err + } ctxt = context.WithValue(ctxt, TXSimulatorKey, txsim) + ctxt = context.WithValue(ctxt, HistoryQueryExecutorKey, historyQueryExecutor) return ctxt, txsim, nil } @@ -1137,6 +1142,59 @@ func TestQueries(t *testing.T) { //Reset the query limit to default viper.Set("ledger.state.queryLimit", 10000) + if ledgerconfig.IsHistoryDBEnabled() == true { + + f = "put" + args = util.ToChaincodeArgs(f, "marble12", "{\"docType\":\"marble\",\"name\":\"marble12\",\"color\":\"red\",\"size\":30,\"owner\":\"jerry\"}") + spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}} + _, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber) + nextBlockNumber++ + if err != nil { + t.Fail() + t.Logf("Error invoking <%s>: %s", ccID, err) + theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec}) + return + } + + f = "put" + args = util.ToChaincodeArgs(f, "marble12", "{\"docType\":\"marble\",\"name\":\"marble12\",\"color\":\"red\",\"size\":30,\"owner\":\"jerry\"}") + spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}} + _, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber) + nextBlockNumber++ + if err != nil { + t.Fail() + t.Logf("Error invoking <%s>: %s", ccID, err) + theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec}) + return + } + + //The following history query for "marble12" should return 3 records + f = "history" + args = util.ToChaincodeArgs(f, "marble12") + + spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}} + _, _, retval, err := invoke(ctxt, chainID, spec, nextBlockNumber) + nextBlockNumber++ + if err != nil { + t.Fail() + t.Logf("Error invoking <%s>: %s", ccID, err) + theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec}) + return + } + + var history []interface{} + err = json.Unmarshal(retval, &history) + + //default query limit of 10000 is used, query should return all records that meet the criteria + if len(history) != 3 { + t.Fail() + t.Logf("Error detected with the history query, should have returned 3 but returned %v", len(keys)) + theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec}) + return + } + + } + if ledgerconfig.IsCouchDBEnabled() == true { //The following rich query for should return 9 marbles diff --git a/core/chaincode/handler.go b/core/chaincode/handler.go index 908763024aa..37a08ce0e2c 100644 --- a/core/chaincode/handler.go +++ b/core/chaincode/handler.go @@ -18,6 +18,7 @@ package chaincode import ( "bytes" + "encoding/gob" "fmt" "io" "sync" @@ -193,7 +194,8 @@ func (handler *Handler) createTxContext(ctxt context.Context, chainID string, tx if handler.txCtxs[txid] != nil { return nil, fmt.Errorf("txid:%s exists", txid) } - txctx := &transactionContext{chainID: chainID, signedProp: signedProp, proposal: prop, responseNotifier: make(chan *pb.ChaincodeMessage, 1), + txctx := &transactionContext{chainID: chainID, signedProp: signedProp, + proposal: prop, responseNotifier: make(chan *pb.ChaincodeMessage, 1), queryIteratorMap: make(map[string]commonledger.ResultsIterator)} handler.txCtxs[txid] = txctx txctx.txsimulator = getTxSimulator(ctxt) @@ -720,38 +722,20 @@ func (handler *Handler) handleGetStateByRange(msg *pb.ChaincodeMessage) { } handler.putQueryIterator(txContext, iterID, rangeIter) + var payload *pb.QueryResponse + payload, err = getQueryResponse(handler, txContext, rangeIter, iterID) - var keysAndValues []*pb.QueryStateKeyValue - var i = 0 - var queryLimit = ledgerconfig.GetQueryLimit() - var qresult commonledger.QueryResult - for ; i < queryLimit; i++ { - qresult, err = rangeIter.Next() - if err != nil { - chaincodeLogger.Errorf("Failed to get query result from iterator. Sending %s", pb.ChaincodeMessage_ERROR) - return - } - if qresult == nil { - break - } - kv := qresult.(*ledger.KV) - keyAndValue := pb.QueryStateKeyValue{Key: kv.Key, Value: kv.Value} - keysAndValues = append(keysAndValues, &keyAndValue) - } - if qresult != nil { + if err != nil { rangeIter.Close() handler.deleteQueryIterator(txContext, iterID) - //TODO log the warning that the queryLimit was exceeded. this will need to be revisited - //following changes to the future paging design. - chaincodeLogger.Warningf("Query limit of %v was exceeded. Not all values meeting the criteria were returned.", queryLimit) + payload := []byte(err.Error()) + chaincodeLogger.Errorf("Failed to get query result. Sending %s", pb.ChaincodeMessage_ERROR) + serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + return } - //TODO - HasMore is set to false until the requery issue for the peer is resolved - //FAB-2462 - Re-introduce paging for range queries and rich queries - //payload := &pb.QueryStateResponse{KeysAndValues: keysAndValues, HasMore: qresult != nil, Id: iterID} - - payload := &pb.QueryStateResponse{KeysAndValues: keysAndValues, HasMore: false, Id: iterID} - payloadBytes, err := proto.Marshal(payload) + var payloadBytes []byte + payloadBytes, err = proto.Marshal(payload) if err != nil { rangeIter.Close() handler.deleteQueryIterator(txContext, iterID) @@ -769,6 +753,60 @@ func (handler *Handler) handleGetStateByRange(msg *pb.ChaincodeMessage) { }() } +func getBytes(qresult interface{}) ([]byte, error) { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(qresult) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +//getQueryResponse takes an iterator and fetch state to construct QueryResponse +func getQueryResponse(handler *Handler, txContext *transactionContext, iter commonledger.ResultsIterator, + iterID string) (*pb.QueryResponse, error) { + + var i = 0 + var err error + var queryLimit = ledgerconfig.GetQueryLimit() + var queryResult commonledger.QueryResult + var queryResultsBytes []*pb.QueryResultBytes + + for ; i < queryLimit; i++ { + queryResult, err = iter.Next() + if err != nil { + return nil, err + } + if queryResult == nil { + break + } + var resultBytes []byte + resultBytes, err = getBytes(queryResult) + if err != nil { + return nil, err + } + + qresultBytes := pb.QueryResultBytes{ResultBytes: resultBytes} + queryResultsBytes = append(queryResultsBytes, &qresultBytes) + } + + if queryResult == nil { + iter.Close() + handler.deleteQueryIterator(txContext, iterID) + } else { + //TODO: remove this else part completely when paging design is implemented + //FAB-2462 - Re-introduce paging for range queries and rich queries + chaincodeLogger.Warningf("Query limit of %v was exceeded. Not all values meeting the criteria were returned.", queryLimit) + iter.Close() + handler.deleteQueryIterator(txContext, iterID) + } + //TODO - HasMore is set to false until the requery issue for the peer is resolved + //FAB-2462 - Re-introduce paging for range queries and rich queries + //payload := &pb.QueryResponse{Data: data, HasMore: qresult != nil, Id: iterID} + return &pb.QueryResponse{Results: queryResultsBytes, HasMore: false, Id: iterID}, nil +} + // afterQueryStateNext handles a QUERY_STATE_NEXT request from the chaincode. func (handler *Handler) afterQueryStateNext(e *fsm.Event, state string) { msg, ok := e.Args[0].(*pb.ChaincodeMessage) @@ -824,39 +862,17 @@ func (handler *Handler) handleQueryStateNext(msg *pb.ChaincodeMessage) { return } - var keysAndValues []*pb.QueryStateKeyValue - var i = 0 - var queryLimit = ledgerconfig.GetQueryLimit() - - var qresult commonledger.QueryResult - var err error - for ; i < queryLimit; i++ { - qresult, err = queryIter.Next() - if err != nil { - chaincodeLogger.Errorf("Failed to get query result from iterator. Sending %s", pb.ChaincodeMessage_ERROR) - return - } - if qresult != nil { - break - } - kv := qresult.(*ledger.KV) - keyAndValue := pb.QueryStateKeyValue{Key: kv.Key, Value: kv.Value} - keysAndValues = append(keysAndValues, &keyAndValue) - } + payload, err := getQueryResponse(handler, txContext, queryIter, queryStateNext.Id) - if qresult != nil { + if err != nil { queryIter.Close() handler.deleteQueryIterator(txContext, queryStateNext.Id) - - //TODO log the warning that the queryLimit was exceeded. this will need to be revisited - //following changes to the future paging design. - chaincodeLogger.Warningf("Query limit of %v was exceeded. Not all values meeting the criteria were returned.", queryLimit) + payload := []byte(err.Error()) + chaincodeLogger.Errorf("Failed to get query result. Sending %s", pb.ChaincodeMessage_ERROR) + serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + return } - //TODO - HasMore is set to false until the requery issue for the peer is resolved - //FAB-2462 - Re-introduce paging for range queries and rich queries - //payload := &pb.QueryStateResponse{KeysAndValues: keysAndValues, HasMore: qresult != nil, Id: queryStateNext.Id} - payload := &pb.QueryStateResponse{KeysAndValues: keysAndValues, HasMore: false, Id: queryStateNext.Id} payloadBytes, err := proto.Marshal(payload) if err != nil { queryIter.Close() @@ -927,7 +943,7 @@ func (handler *Handler) handleQueryStateClose(msg *pb.ChaincodeMessage) { handler.deleteQueryIterator(txContext, queryStateClose.Id) } - payload := &pb.QueryStateResponse{HasMore: false, Id: queryStateClose.Id} + payload := &pb.QueryResponse{HasMore: false, Id: queryStateClose.Id} payloadBytes, err := proto.Marshal(payload) if err != nil { @@ -1010,40 +1026,19 @@ func (handler *Handler) handleGetQueryResult(msg *pb.ChaincodeMessage) { } handler.putQueryIterator(txContext, iterID, executeIter) + var payload *pb.QueryResponse + payload, err = getQueryResponse(handler, txContext, executeIter, iterID) - var keysAndValues []*pb.QueryStateKeyValue - var i = 0 - var queryLimit = ledgerconfig.GetQueryLimit() - var qresult commonledger.QueryResult - for ; i < queryLimit; i++ { - qresult, err = executeIter.Next() - if err != nil { - chaincodeLogger.Errorf("Failed to get query result from iterator. Sending %s", pb.ChaincodeMessage_ERROR) - return - } - if qresult == nil { - break - } - queryRecord := qresult.(*ledger.QueryRecord) - keyAndValue := pb.QueryStateKeyValue{Key: queryRecord.Key, Value: queryRecord.Record} - keysAndValues = append(keysAndValues, &keyAndValue) - } - - if qresult != nil { + if err != nil { executeIter.Close() handler.deleteQueryIterator(txContext, iterID) - - //TODO log the warning that the queryLimit was exceeded. this will need to be revisited - //following changes to the future paging design. - chaincodeLogger.Warningf("Query limit of %v was exceeded. Not all values meeting the criteria were returned.", queryLimit) + payload := []byte(err.Error()) + chaincodeLogger.Errorf("Failed to get query result. Sending %s", pb.ChaincodeMessage_ERROR) + serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + return } var payloadBytes []byte - - //TODO - HasMore is set to false until the requery issue for the peer is resolved - //FAB-2462 - Re-introduce paging for range queries and rich queries - //payload := &pb.QueryStateResponse{KeysAndValues: keysAndValues, HasMore: qresult != nil, Id: iterID} - payload := &pb.QueryStateResponse{KeysAndValues: keysAndValues, HasMore: false, Id: iterID} payloadBytes, err = proto.Marshal(payload) if err != nil { executeIter.Close() @@ -1051,7 +1046,7 @@ func (handler *Handler) handleGetQueryResult(msg *pb.ChaincodeMessage) { // Send error msg back to chaincode. GetState will not trigger event payload := []byte(err.Error()) - chaincodeLogger.Errorf("Failed marshall resopnse. Sending %s", pb.ChaincodeMessage_ERROR) + chaincodeLogger.Errorf("Failed marshall response. Sending %s", pb.ChaincodeMessage_ERROR) serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} return } @@ -1128,41 +1123,19 @@ func (handler *Handler) handleGetHistoryForKey(msg *pb.ChaincodeMessage) { handler.putQueryIterator(txContext, iterID, historyIter) - // TODO QueryStateKeyValue can be re-used for now since history records have a string (TxID) - // and value (value). But we'll need to use another structure if we add other fields like timestamp. - var keysAndValues []*pb.QueryStateKeyValue - var i = 0 - var queryLimit = ledgerconfig.GetQueryLimit() - var qresult commonledger.QueryResult - for ; i < queryLimit; i++ { - qresult, err = historyIter.Next() - if err != nil { - chaincodeLogger.Errorf("Failed to get query result from iterator. Sending %s", pb.ChaincodeMessage_ERROR) - return - } - if qresult == nil { - break - } - queryRecord := qresult.(*ledger.KeyModification) - keyAndValue := pb.QueryStateKeyValue{Key: queryRecord.TxID, Value: queryRecord.Value} - keysAndValues = append(keysAndValues, &keyAndValue) - } + var payload *pb.QueryResponse + payload, err = getQueryResponse(handler, txContext, historyIter, iterID) - if qresult != nil { + if err != nil { historyIter.Close() handler.deleteQueryIterator(txContext, iterID) - - //TODO log the warning that the queryLimit was exceeded. this will need to be revisited - //following changes to the future paging design. - chaincodeLogger.Warningf("Query limit of %v was exceeded. Not all values meeting the criteria were returned.", queryLimit) + payload := []byte(err.Error()) + chaincodeLogger.Errorf("Failed to get query result. Sending %s", pb.ChaincodeMessage_ERROR) + serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + return } var payloadBytes []byte - - //TODO - HasMore is set to false until the requery issue for the peer is resolved - //FAB-2462 - Re-introduce paging for range queries and rich queries - //payload := &pb.QueryStateResponse{KeysAndValues: keysAndValues, HasMore: qresult != nil, Id: iterID} - payload := &pb.QueryStateResponse{KeysAndValues: keysAndValues, HasMore: false, Id: iterID} payloadBytes, err = proto.Marshal(payload) if err != nil { historyIter.Close() diff --git a/core/chaincode/shim/chaincode.go b/core/chaincode/shim/chaincode.go index 773cc822fc5..fc6b74186ae 100644 --- a/core/chaincode/shim/chaincode.go +++ b/core/chaincode/shim/chaincode.go @@ -19,6 +19,8 @@ limitations under the License. package shim import ( + "bytes" + "encoding/gob" "errors" "flag" "fmt" @@ -31,7 +33,9 @@ import ( "github.com/golang/protobuf/ptypes/timestamp" "github.com/hyperledger/fabric/bccsp/factory" "github.com/hyperledger/fabric/common/flogging" + commonledger "github.com/hyperledger/fabric/common/ledger" "github.com/hyperledger/fabric/core/comm" + "github.com/hyperledger/fabric/core/ledger" pb "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" "github.com/op/go-logging" @@ -348,15 +352,34 @@ func (stub *ChaincodeStub) DelState(key string) error { return stub.handler.handleDelState(key, stub.TxID) } -// StateQueryIterator allows a chaincode to iterate over a set of +// CommonIterator allows a chaincode to iterate over a set of // key/value pairs in the state. -type StateQueryIterator struct { +type CommonIterator struct { handler *Handler uuid string - response *pb.QueryStateResponse + response *pb.QueryResponse currentLoc int } +//GeneralQueryIterator allows a chaincode to iterate the result +//of range and execute query. +type StateQueryIterator struct { + *CommonIterator +} + +//GeneralQueryIterator allows a chaincode to iterate the result +//of history query. +type HistoryQueryIterator struct { + *CommonIterator +} + +type resultType uint8 + +const ( + STATE_QUERY_RESULT resultType = iota + 1 + HISTORY_QUERY_RESULT +) + // GetStateByRange function can be invoked by a chaincode to query of a range // of keys in the state. Assuming the startKey and endKey are in lexical order, // an iterator will be returned that can be used to iterate over all keys @@ -367,7 +390,7 @@ func (stub *ChaincodeStub) GetStateByRange(startKey, endKey string) (StateQueryI if err != nil { return nil, err } - return &StateQueryIterator{stub.handler, stub.TxID, response, 0}, nil + return &StateQueryIterator{CommonIterator: &CommonIterator{stub.handler, stub.TxID, response, 0}}, nil } // GetQueryResult function can be invoked by a chaincode to perform a @@ -380,17 +403,17 @@ func (stub *ChaincodeStub) GetQueryResult(query string) (StateQueryIteratorInter if err != nil { return nil, err } - return &StateQueryIterator{stub.handler, stub.TxID, response, 0}, nil + return &StateQueryIterator{CommonIterator: &CommonIterator{stub.handler, stub.TxID, response, 0}}, nil } // GetHistoryForKey function can be invoked by a chaincode to return a history of // key values across time. GetHistoryForKey is intended to be used for read-only queries. -func (stub *ChaincodeStub) GetHistoryForKey(key string) (StateQueryIteratorInterface, error) { +func (stub *ChaincodeStub) GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error) { response, err := stub.handler.handleGetHistoryForKey(key, stub.TxID) if err != nil { return nil, err } - return &StateQueryIterator{stub.handler, stub.TxID, response, 0}, nil + return &HistoryQueryIterator{CommonIterator: &CommonIterator{stub.handler, stub.TxID, response, 0}}, nil } //CreateCompositeKey combines the given attributes to form a composite key. @@ -461,42 +484,80 @@ func getStateByPartialCompositeKey(stub ChaincodeStubInterface, objectType strin return keysIter, nil } +func (iter *StateQueryIterator) Next() (*ledger.KV, error) { + result, err := next(iter.CommonIterator, STATE_QUERY_RESULT) + return result.(*ledger.KV), err +} + +func (iter *HistoryQueryIterator) Next() (*ledger.KeyModification, error) { + result, err := next(iter.CommonIterator, HISTORY_QUERY_RESULT) + return result.(*ledger.KeyModification), err +} + // HasNext returns true if the range query iterator contains additional keys // and values. -func (iter *StateQueryIterator) HasNext() bool { - if iter.currentLoc < len(iter.response.KeysAndValues) || iter.response.HasMore { +func (iter *CommonIterator) HasNext() bool { + if iter.currentLoc < len(iter.response.Results) || iter.response.HasMore { return true } return false } -// Next returns the next key and value in the range query iterator. -func (iter *StateQueryIterator) Next() (string, []byte, error) { - if iter.currentLoc < len(iter.response.KeysAndValues) { - keyValue := iter.response.KeysAndValues[iter.currentLoc] - iter.currentLoc++ - return keyValue.Key, keyValue.Value, nil - } else if !iter.response.HasMore { - return "", nil, errors.New("No such key") - } else { - response, err := iter.handler.handleQueryStateNext(iter.response.Id, iter.uuid) +func getResultFromBytes(queryResultBytes *pb.QueryResultBytes, iter *CommonIterator, + rType resultType) (commonledger.QueryResult, error) { - if err != nil { - return "", nil, err + decoder := gob.NewDecoder(bytes.NewBuffer(queryResultBytes.ResultBytes)) + + if rType == STATE_QUERY_RESULT { + var stateQueryResult ledger.KV + if err := decoder.Decode(&stateQueryResult); err != nil { + return nil, err } + iter.currentLoc++ + return &stateQueryResult, nil - iter.currentLoc = 0 - iter.response = response - keyValue := iter.response.KeysAndValues[iter.currentLoc] + } else if rType == HISTORY_QUERY_RESULT { + var historyQueryResult ledger.KeyModification + if err := decoder.Decode(&historyQueryResult); err != nil { + return nil, err + } iter.currentLoc++ - return keyValue.Key, keyValue.Value, nil + return &historyQueryResult, nil + + } + return nil, errors.New("Wrong result type") +} + +func fetchRemainingQueryResult(iter *CommonIterator) error { + response, err := iter.handler.handleQueryStateNext(iter.response.Id, iter.uuid) + + if err != nil { + return err + } + + iter.currentLoc = 0 + iter.response = response + return nil +} + +// Next returns the next key and value in the state or history query iterator. +func next(iter *CommonIterator, rType resultType) (commonledger.QueryResult, error) { + if iter.currentLoc < len(iter.response.Results) { + return getResultFromBytes(iter.response.Results[iter.currentLoc], iter, rType) + } else if !iter.response.HasMore { + return nil, errors.New("No such key") + } + if err := fetchRemainingQueryResult(iter); err != nil { + return nil, err } + return getResultFromBytes(iter.response.Results[iter.currentLoc], iter, rType) + } // Close closes the range query iterator. This should be called when done // reading from the iterator to free up resources. -func (iter *StateQueryIterator) Close() error { +func (iter *CommonIterator) Close() error { _, err := iter.handler.handleQueryStateClose(iter.response.Id, iter.uuid) return err } diff --git a/core/chaincode/shim/handler.go b/core/chaincode/shim/handler.go index a5ce0a12e8e..7615d80d78e 100644 --- a/core/chaincode/shim/handler.go +++ b/core/chaincode/shim/handler.go @@ -506,7 +506,7 @@ func (handler *Handler) handleDelState(key string, txid string) error { return errors.New("Incorrect chaincode message received") } -func (handler *Handler) handleGetStateByRange(startKey, endKey string, txid string) (*pb.QueryStateResponse, error) { +func (handler *Handler) handleGetStateByRange(startKey, endKey string, txid string) (*pb.QueryResponse, error) { // Create the channel on which to communicate the response from validating peer respChan, uniqueReqErr := handler.createChannel(txid) if uniqueReqErr != nil { @@ -534,7 +534,7 @@ func (handler *Handler) handleGetStateByRange(startKey, endKey string, txid stri // Success response chaincodeLogger.Debugf("[%s]Received %s. Successfully got range", shorttxid(responseMsg.Txid), pb.ChaincodeMessage_RESPONSE) - rangeQueryResponse := &pb.QueryStateResponse{} + rangeQueryResponse := &pb.QueryResponse{} unmarshalErr := proto.Unmarshal(responseMsg.Payload, rangeQueryResponse) if unmarshalErr != nil { chaincodeLogger.Errorf("[%s]unmarshall error", shorttxid(responseMsg.Txid)) @@ -554,7 +554,7 @@ func (handler *Handler) handleGetStateByRange(startKey, endKey string, txid stri return nil, errors.New("Incorrect chaincode message received") } -func (handler *Handler) handleQueryStateNext(id, txid string) (*pb.QueryStateResponse, error) { +func (handler *Handler) handleQueryStateNext(id, txid string) (*pb.QueryResponse, error) { // Create the channel on which to communicate the response from validating peer respChan, uniqueReqErr := handler.createChannel(txid) if uniqueReqErr != nil { @@ -582,11 +582,11 @@ func (handler *Handler) handleQueryStateNext(id, txid string) (*pb.QueryStateRes // Success response chaincodeLogger.Debugf("[%s]Received %s. Successfully got range", shorttxid(responseMsg.Txid), pb.ChaincodeMessage_RESPONSE) - queryResponse := &pb.QueryStateResponse{} + queryResponse := &pb.QueryResponse{} unmarshalErr := proto.Unmarshal(responseMsg.Payload, queryResponse) if unmarshalErr != nil { chaincodeLogger.Errorf("[%s]unmarshall error", shorttxid(responseMsg.Txid)) - return nil, errors.New("Error unmarshalling QueryStateResponse.") + return nil, errors.New("Error unmarshalling QueryResponse.") } return queryResponse, nil @@ -602,7 +602,7 @@ func (handler *Handler) handleQueryStateNext(id, txid string) (*pb.QueryStateRes return nil, errors.New("Incorrect chaincode message received") } -func (handler *Handler) handleQueryStateClose(id, txid string) (*pb.QueryStateResponse, error) { +func (handler *Handler) handleQueryStateClose(id, txid string) (*pb.QueryResponse, error) { // Create the channel on which to communicate the response from validating peer respChan, uniqueReqErr := handler.createChannel(txid) if uniqueReqErr != nil { @@ -630,11 +630,11 @@ func (handler *Handler) handleQueryStateClose(id, txid string) (*pb.QueryStateRe // Success response chaincodeLogger.Debugf("[%s]Received %s. Successfully got range", shorttxid(responseMsg.Txid), pb.ChaincodeMessage_RESPONSE) - queryResponse := &pb.QueryStateResponse{} + queryResponse := &pb.QueryResponse{} unmarshalErr := proto.Unmarshal(responseMsg.Payload, queryResponse) if unmarshalErr != nil { chaincodeLogger.Errorf("[%s]unmarshall error", shorttxid(responseMsg.Txid)) - return nil, errors.New("Error unmarshalling QueryStateResponse.") + return nil, errors.New("Error unmarshalling QueryResponse.") } return queryResponse, nil @@ -650,7 +650,7 @@ func (handler *Handler) handleQueryStateClose(id, txid string) (*pb.QueryStateRe return nil, errors.New("Incorrect chaincode message received") } -func (handler *Handler) handleGetQueryResult(query string, txid string) (*pb.QueryStateResponse, error) { +func (handler *Handler) handleGetQueryResult(query string, txid string) (*pb.QueryResponse, error) { // Create the channel on which to communicate the response from validating peer respChan, uniqueReqErr := handler.createChannel(txid) if uniqueReqErr != nil { @@ -678,11 +678,11 @@ func (handler *Handler) handleGetQueryResult(query string, txid string) (*pb.Que // Success response chaincodeLogger.Debugf("[%s]Received %s. Successfully got range", shorttxid(responseMsg.Txid), pb.ChaincodeMessage_RESPONSE) - executeQueryResponse := &pb.QueryStateResponse{} + executeQueryResponse := &pb.QueryResponse{} unmarshalErr := proto.Unmarshal(responseMsg.Payload, executeQueryResponse) if unmarshalErr != nil { chaincodeLogger.Errorf("[%s]unmarshall error", shorttxid(responseMsg.Txid)) - return nil, errors.New("Error unmarshalling QueryStateResponse.") + return nil, errors.New("Error unmarshalling QueryResponse.") } return executeQueryResponse, nil @@ -698,7 +698,7 @@ func (handler *Handler) handleGetQueryResult(query string, txid string) (*pb.Que return nil, errors.New("Incorrect chaincode message received") } -func (handler *Handler) handleGetHistoryForKey(key string, txid string) (*pb.QueryStateResponse, error) { +func (handler *Handler) handleGetHistoryForKey(key string, txid string) (*pb.QueryResponse, error) { // Create the channel on which to communicate the response from validating peer respChan, uniqueReqErr := handler.createChannel(txid) if uniqueReqErr != nil { @@ -726,11 +726,11 @@ func (handler *Handler) handleGetHistoryForKey(key string, txid string) (*pb.Que // Success response chaincodeLogger.Debugf("[%s]Received %s. Successfully got range", shorttxid(responseMsg.Txid), pb.ChaincodeMessage_RESPONSE) - getHistoryForKeyResponse := &pb.QueryStateResponse{} + getHistoryForKeyResponse := &pb.QueryResponse{} unmarshalErr := proto.Unmarshal(responseMsg.Payload, getHistoryForKeyResponse) if unmarshalErr != nil { chaincodeLogger.Errorf("[%s]unmarshall error", shorttxid(responseMsg.Txid)) - return nil, errors.New("Error unmarshalling QueryStateResponse.") + return nil, errors.New("Error unmarshalling QueryResponse.") } return getHistoryForKeyResponse, nil diff --git a/core/chaincode/shim/interfaces.go b/core/chaincode/shim/interfaces.go index 0a09a0064dd..c9681dca712 100644 --- a/core/chaincode/shim/interfaces.go +++ b/core/chaincode/shim/interfaces.go @@ -19,6 +19,7 @@ package shim import ( "github.com/golang/protobuf/ptypes/timestamp" + "github.com/hyperledger/fabric/core/ledger" pb "github.com/hyperledger/fabric/protos/peer" ) @@ -99,7 +100,7 @@ type ChaincodeStubInterface interface { // GetHistoryForKey function can be invoked by a chaincode to return a history of // key values across time. GetHistoryForKey is intended to be used for read-only queries. - GetHistoryForKey(key string) (StateQueryIteratorInterface, error) + GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error) // GetCreator returns SignatureHeader.Creator of the proposal // this Stub refers to. @@ -127,18 +128,42 @@ type ChaincodeStubInterface interface { SetEvent(name string, payload []byte) error } -// StateQueryIteratorInterface allows a chaincode to iterate over a set of -// key/value pairs in the state. -type StateQueryIteratorInterface interface { - +// CommonIteratorInterface allows a chaincode to check whether any more result +//to be fetched from an iterate and close it when needed. +type CommonIteratorInterface interface { // HasNext returns true if the range query iterator contains additional keys // and values. HasNext() bool - // Next returns the next key and value in the range query iterator. - Next() (string, []byte, error) - // Close closes the range query iterator. This should be called when done // reading from the iterator to free up resources. Close() error } + +// StateQueryIteratorInterface allows a chaincode to iterate over a set of +// key/value pairs returned by range and execute query. +type StateQueryIteratorInterface interface { + // Inherit HasNext() and Close() + CommonIteratorInterface + + // Next returns the next key and value in the range and execute query iterator. + Next() (*ledger.KV, error) +} + +// HistoryQueryIteratorInterface allows a chaincode to iterate over a set of +// key/value pairs returned by a history query. +type HistoryQueryIteratorInterface interface { + // Inherit HasNext() and Close() + CommonIteratorInterface + + // Next returns the next key and value in the history query iterator. + Next() (*ledger.KeyModification, error) +} + +// MockQueryIteratorInterface allows a chaincode to iterate over a set of +// key/value pairs returned by range query. +// TODO: Once the execute query and history query are implemented in MockStub, +// we need to update this interface +type MockQueryIteratorInterface interface { + StateQueryIteratorInterface +} diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeStub.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeStub.java index 1d7f6237330..fda8e1c3220 100644 --- a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeStub.java +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeStub.java @@ -75,7 +75,6 @@ public void putState(String key, String value) { public void delState(String key) { handler.handleDeleteState(key, uuid); } - /** * Given a start key and end key, this method returns a map of items with value converted to UTF-8 string. * @@ -83,6 +82,8 @@ public void delState(String key) { * @param endKey * @return */ + //TODO: Uncomment and fix range query with new proto type + /* public Map getStateByRange(String startKey, String endKey) { Map retMap = new HashMap<>(); for (Map.Entry item : getStateByRangeRaw(startKey, endKey).entrySet()) { @@ -90,7 +91,7 @@ public Map getStateByRange(String startKey, String endKey) { } return retMap; } - + */ /** * This method is same as getStateByRange, except it returns value in ByteString, useful in cases where * serialized object can be retrieved. @@ -99,6 +100,8 @@ public Map getStateByRange(String startKey, String endKey) { * @param endKey * @return */ + //TODO: Uncomment and fix range query with new proto type + /* public Map getStateByRangeRaw(String startKey, String endKey) { Map map = new HashMap<>(); for (ChaincodeShim.QueryStateKeyValue mapping : handler.handleGetStateByRange( @@ -107,6 +110,7 @@ public Map getStateByRangeRaw(String startKey, String endKey } return map; } + */ /** * Given a partial composite key, this method returns a map of items (whose key's prefix @@ -118,11 +122,15 @@ public Map getStateByRangeRaw(String startKey, String endKey * @param endKey * @return */ + + //TODO: Uncomment and fix range query with new proto type + /* public Map getStateByPartialCompositeKey(String objectType, String[] attributes) { String partialCompositeKey = new String(); partialCompositeKey = createCompositeKey(objectType, attributes); return getStateByRange(partialCompositeKey+"1", partialCompositeKey+":"); } + */ /** * Given a set of attributes, this method combines these attributes to return a composite key. diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java index 05573da32ad..d20dd1d4eb5 100644 --- a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java @@ -600,6 +600,8 @@ public void handleDeleteState(String key, String uuid) { } } + //TODO: Uncomment and fix range query with new proto type +/* public QueryStateResponse handleGetStateByRange(String startKey, String endKey, String uuid) { // Create the channel on which to communicate the response from validating peer Channel responseChannel; @@ -673,7 +675,7 @@ public QueryStateResponse handleGetStateByRange(String startKey, String endKey, deleteChannel(uuid); } } - +*/ public ByteString handleInvokeChaincode(String chaincodeName, String function, List args, String uuid) { // Check if this is a transaction if (!isTransaction.containsKey(uuid)) { diff --git a/core/chaincode/shim/mockstub.go b/core/chaincode/shim/mockstub.go index 444fc730971..2ee01aee9a1 100644 --- a/core/chaincode/shim/mockstub.go +++ b/core/chaincode/shim/mockstub.go @@ -26,6 +26,7 @@ import ( "github.com/golang/protobuf/ptypes/timestamp" "github.com/hyperledger/fabric/common/util" + "github.com/hyperledger/fabric/core/ledger" pb "github.com/hyperledger/fabric/protos/peer" "github.com/op/go-logging" ) @@ -210,7 +211,7 @@ func (stub *MockStub) GetQueryResult(query string) (StateQueryIteratorInterface, // GetHistoryForKey function can be invoked by a chaincode to return a history of // key values across time. GetHistoryForKey is intended to be used for read-only queries. -func (stub *MockStub) GetHistoryForKey(key string) (StateQueryIteratorInterface, error) { +func (stub *MockStub) GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error) { return nil, errors.New("Not Implemented") } @@ -356,15 +357,15 @@ func (iter *MockStateRangeQueryIterator) HasNext() bool { } // Next returns the next key and value in the range query iterator. -func (iter *MockStateRangeQueryIterator) Next() (string, []byte, error) { +func (iter *MockStateRangeQueryIterator) Next() (*ledger.KV, error) { if iter.Closed == true { mockLogger.Error("MockStateRangeQueryIterator.Next() called after Close()") - return "", nil, errors.New("MockStateRangeQueryIterator.Next() called after Close()") + return nil, errors.New("MockStateRangeQueryIterator.Next() called after Close()") } if iter.HasNext() == false { mockLogger.Error("MockStateRangeQueryIterator.Next() called when it does not HaveNext()") - return "", nil, errors.New("MockStateRangeQueryIterator.Next() called when it does not HaveNext()") + return nil, errors.New("MockStateRangeQueryIterator.Next() called when it does not HaveNext()") } for iter.Current != nil { @@ -376,12 +377,12 @@ func (iter *MockStateRangeQueryIterator) Next() (string, []byte, error) { key := iter.Current.Value.(string) value, err := iter.Stub.GetState(key) iter.Current = iter.Current.Next() - return key, value, err + return &ledger.KV{Key: key, Value: value}, err } iter.Current = iter.Current.Next() } mockLogger.Error("MockStateRangeQueryIterator.Next() went past end of range") - return "", nil, errors.New("MockStateRangeQueryIterator.Next() went past end of range") + return nil, errors.New("MockStateRangeQueryIterator.Next() went past end of range") } // Close closes the range query iterator. This should be called when done diff --git a/core/chaincode/shim/mockstub_test.go b/core/chaincode/shim/mockstub_test.go index 32203ed8baa..d1de2255891 100644 --- a/core/chaincode/shim/mockstub_test.go +++ b/core/chaincode/shim/mockstub_test.go @@ -43,14 +43,14 @@ func TestMockStateRangeQueryIterator(t *testing.T) { fmt.Println("Running loop") for i := 0; i < 2; i++ { - key, value, err := rqi.Next() - fmt.Println("Loop", i, "got", key, value, err) - if expectKeys[i] != key { - fmt.Println("Expected key", expectKeys[i], "got", key) + response, err := rqi.Next() + fmt.Println("Loop", i, "got", response.Key, response.Value, err) + if expectKeys[i] != response.Key { + fmt.Println("Expected key", expectKeys[i], "got", response.Key) t.FailNow() } - if expectValues[i][0] != value[0] { - fmt.Println("Expected value", expectValues[i], "got", value) + if expectValues[i][0] != response.Value[0] { + fmt.Println("Expected value", expectValues[i], "got", response.Value) } } } @@ -165,13 +165,13 @@ func TestGetStateByPartialCompositeKey(t *testing.T) { rqi, _ := stub.GetStateByPartialCompositeKey("marble", []string{"set-1"}) fmt.Println("Running loop") for i := 0; i < 2; i++ { - key, value, err := rqi.Next() - fmt.Println("Loop", i, "got", key, value, err) - if expectKeys[i] != key { - fmt.Println("Expected key", expectKeys[i], "got", key) + response, err := rqi.Next() + fmt.Println("Loop", i, "got", response.Key, response.Value, err) + if expectKeys[i] != response.Key { + fmt.Println("Expected key", expectKeys[i], "got", response.Key) t.FailNow() } - objectType, attributes, _ := stub.SplitCompositeKey(key) + objectType, attributes, _ := stub.SplitCompositeKey(response.Key) if objectType != "marble" { fmt.Println("Expected objectType", "marble", "got", objectType) t.FailNow() @@ -183,8 +183,8 @@ func TestGetStateByPartialCompositeKey(t *testing.T) { t.FailNow() } } - if jsonBytesEqual(expectValues[i], value) != true { - fmt.Println("Expected value", expectValues[i], "got", value) + if jsonBytesEqual(expectValues[i], response.Value) != true { + fmt.Println("Expected value", expectValues[i], "got", response.Value) t.FailNow() } } @@ -210,8 +210,8 @@ func TestGetStateByPartialCompositeKeyCollision(t *testing.T) { fmt.Println("Running loop") for rqi.HasNext() { i++ - key, value, err := rqi.Next() - fmt.Println("Loop", i, "got", key, value, err) + response, err := rqi.Next() + fmt.Println("Loop", i, "got", response.Key, response.Value, err) } // Only the single "Vehicle" object should be returned, not the "VehicleListing" object if i != 1 { diff --git a/core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb_query_executer.go b/core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb_query_executer.go index 2e2ebec61c3..e0a1e8d42f6 100644 --- a/core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb_query_executer.go +++ b/core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb_query_executer.go @@ -87,14 +87,14 @@ func (scanner *historyScanner) Next() (commonledger.QueryResult, error) { return nil, err } - // Get the txid and key write value associated with this transaction - txID, keyValue, err := getTxIDandKeyWriteValueFromTran(tranEnvelope, scanner.namespace, scanner.key) + // Get the txid, key write value, timestamp, and delete indicator associated with this transaction + queryResult, err := getKeyModificationFromTran(tranEnvelope, scanner.namespace, scanner.key) if err != nil { return nil, err } logger.Debugf("Found historic key value for namespace:%s key:%s from transaction %s\n", - scanner.namespace, scanner.key, txID) - return &ledger.KeyModification{TxID: txID, Value: keyValue}, nil + scanner.namespace, scanner.key, queryResult.(*ledger.KeyModification).TxID) + return queryResult, nil } func (scanner *historyScanner) Close() { @@ -102,39 +102,39 @@ func (scanner *historyScanner) Close() { } // getTxIDandKeyWriteValueFromTran inspects a transaction for writes to a given key -func getTxIDandKeyWriteValueFromTran( - tranEnvelope *common.Envelope, namespace string, key string) (string, []byte, error) { - logger.Debugf("Entering getTxIDandKeyWriteValueFromTran()\n", namespace, key) +func getKeyModificationFromTran(tranEnvelope *common.Envelope, namespace string, key string) (commonledger.QueryResult, error) { + logger.Debugf("Entering getKeyModificationFromTran()\n", namespace, key) // extract action from the envelope payload, err := putils.GetPayload(tranEnvelope) if err != nil { - return "", nil, err + return nil, err } tx, err := putils.GetTransaction(payload.Data) if err != nil { - return "", nil, err + return nil, err } _, respPayload, err := putils.GetPayloads(tx.Actions[0]) if err != nil { - return "", nil, err + return nil, err } chdr, err := putils.UnmarshalChannelHeader(payload.Header.ChannelHeader) if err != nil { - return "", nil, err + return nil, err } txID := chdr.TxId + timestamp := chdr.Timestamp txRWSet := &rwsetutil.TxRwSet{} // Get the Result from the Action and then Unmarshal // it into a TxReadWriteSet using custom unmarshalling if err = txRWSet.FromProtoBytes(respPayload.Results); err != nil { - return txID, nil, err + return nil, err } // look for the namespace and key by looping through the transaction's ReadWriteSets @@ -143,12 +143,13 @@ func getTxIDandKeyWriteValueFromTran( // got the correct namespace, now find the key write for _, kvWrite := range nsRWSet.KvRwSet.Writes { if kvWrite.Key == key { - return txID, kvWrite.Value, nil + return &ledger.KeyModification{TxID: txID, Value: kvWrite.Value, + Timestamp: timestamp, IsDelete: kvWrite.IsDelete}, nil } } // end keys loop - return txID, nil, errors.New("Key not found in namespace's writeset") + return nil, errors.New("Key not found in namespace's writeset") } // end if } //end namespaces loop - return txID, nil, errors.New("Namespace not found in transaction's ReadWriteSets") + return nil, errors.New("Namespace not found in transaction's ReadWriteSets") } diff --git a/core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb_test.go b/core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb_test.go index b8741d6241a..f46573f43bf 100644 --- a/core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb_test.go +++ b/core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb_test.go @@ -105,6 +105,18 @@ func TestHistory(t *testing.T) { err = env.testHistoryDB.Commit(block2) testutil.AssertNoError(t, err, "") + //block3 + simulator, _ = env.txmgr.NewTxSimulator() + simulator.DeleteState("ns1", "key7") + simulator.Done() + simRes, _ = simulator.GetTxSimulationResults() + block3 := bg.NextBlock([][]byte{simRes}, false) + err = store1.AddBlock(block3) + testutil.AssertNoError(t, err, "") + err = env.testHistoryDB.Commit(block3) + testutil.AssertNoError(t, err, "") + t.Logf("Inserted all 3 blocks") + qhistory, err := env.testHistoryDB.NewHistoryQueryExecutor(store1) testutil.AssertNoError(t, err, "Error upon NewHistoryQueryExecutor") @@ -119,12 +131,23 @@ func TestHistory(t *testing.T) { } txid := kmod.(*ledger.KeyModification).TxID retrievedValue := kmod.(*ledger.KeyModification).Value - t.Logf("Retrieved history record for key=key7 at TxId=%s with value %v", txid, retrievedValue) + retrievedTimestamp := kmod.(*ledger.KeyModification).Timestamp + retrievedIsDelete := kmod.(*ledger.KeyModification).IsDelete + t.Logf("Retrieved history record for key=key7 at TxId=%s with value %v and timestamp %v", + txid, retrievedValue, retrievedTimestamp) count++ - expectedValue := []byte("value" + strconv.Itoa(count)) - testutil.AssertEquals(t, retrievedValue, expectedValue) + if count != 4 { + expectedValue := []byte("value" + strconv.Itoa(count)) + testutil.AssertEquals(t, retrievedValue, expectedValue) + testutil.AssertNotEquals(t, retrievedTimestamp, nil) + testutil.AssertEquals(t, retrievedIsDelete, false) + } else { + testutil.AssertEquals(t, retrievedValue, nil) + testutil.AssertNotEquals(t, retrievedTimestamp, nil) + testutil.AssertEquals(t, retrievedIsDelete, true) + } } - testutil.AssertEquals(t, count, 3) + testutil.AssertEquals(t, count, 4) } func TestHistoryForInvalidTran(t *testing.T) { diff --git a/core/ledger/kvledger/txmgmt/statedb/commontests/test_common.go b/core/ledger/kvledger/txmgmt/statedb/commontests/test_common.go index 8b056b2ed9d..bc706ac8309 100644 --- a/core/ledger/kvledger/txmgmt/statedb/commontests/test_common.go +++ b/core/ledger/kvledger/txmgmt/statedb/commontests/test_common.go @@ -234,8 +234,8 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) { queryResult1, err := itr.Next() testutil.AssertNoError(t, err, "") testutil.AssertNotNil(t, queryResult1) - versionedQueryRecord := queryResult1.(*statedb.VersionedQueryRecord) - stringRecord := string(versionedQueryRecord.Record) + versionedQueryRecord := queryResult1.(*statedb.VersionedKV) + stringRecord := string(versionedQueryRecord.Value) bFoundRecord := strings.Contains(stringRecord, "jerry") testutil.AssertEquals(t, bFoundRecord, true) @@ -252,8 +252,8 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) { queryResult1, err = itr.Next() testutil.AssertNoError(t, err, "") testutil.AssertNotNil(t, queryResult1) - versionedQueryRecord = queryResult1.(*statedb.VersionedQueryRecord) - stringRecord = string(versionedQueryRecord.Record) + versionedQueryRecord = queryResult1.(*statedb.VersionedKV) + stringRecord = string(versionedQueryRecord.Value) bFoundRecord = strings.Contains(stringRecord, "jerry") testutil.AssertEquals(t, bFoundRecord, true) @@ -292,8 +292,8 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) { queryResult1, err = itr.Next() testutil.AssertNoError(t, err, "") testutil.AssertNotNil(t, queryResult1) - versionedQueryRecord = queryResult1.(*statedb.VersionedQueryRecord) - stringRecord = string(versionedQueryRecord.Record) + versionedQueryRecord = queryResult1.(*statedb.VersionedKV) + stringRecord = string(versionedQueryRecord.Value) bFoundRecord = strings.Contains(stringRecord, "jerry") testutil.AssertEquals(t, bFoundRecord, true) @@ -310,8 +310,8 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) { queryResult1, err = itr.Next() testutil.AssertNoError(t, err, "") testutil.AssertNotNil(t, queryResult1) - versionedQueryRecord = queryResult1.(*statedb.VersionedQueryRecord) - stringRecord = string(versionedQueryRecord.Record) + versionedQueryRecord = queryResult1.(*statedb.VersionedKV) + stringRecord = string(versionedQueryRecord.Value) bFoundRecord = strings.Contains(stringRecord, "jerry") testutil.AssertEquals(t, bFoundRecord, true) @@ -337,8 +337,8 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) { queryResult1, err = itr.Next() testutil.AssertNoError(t, err, "") testutil.AssertNotNil(t, queryResult1) - versionedQueryRecord = queryResult1.(*statedb.VersionedQueryRecord) - stringRecord = string(versionedQueryRecord.Record) + versionedQueryRecord = queryResult1.(*statedb.VersionedKV) + stringRecord = string(versionedQueryRecord.Value) bFoundRecord = strings.Contains(stringRecord, "fred") testutil.AssertEquals(t, bFoundRecord, true) @@ -355,8 +355,8 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) { queryResult1, err = itr.Next() testutil.AssertNoError(t, err, "") testutil.AssertNotNil(t, queryResult1) - versionedQueryRecord = queryResult1.(*statedb.VersionedQueryRecord) - stringRecord = string(versionedQueryRecord.Record) + versionedQueryRecord = queryResult1.(*statedb.VersionedKV) + stringRecord = string(versionedQueryRecord.Value) bFoundRecord = strings.Contains(stringRecord, "fred") testutil.AssertEquals(t, bFoundRecord, true) @@ -382,8 +382,8 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) { queryResult1, err = itr.Next() testutil.AssertNoError(t, err, "") testutil.AssertNotNil(t, queryResult1) - versionedQueryRecord = queryResult1.(*statedb.VersionedQueryRecord) - stringRecord = string(versionedQueryRecord.Record) + versionedQueryRecord = queryResult1.(*statedb.VersionedKV) + stringRecord = string(versionedQueryRecord.Value) bFoundRecord = strings.Contains(stringRecord, "green") testutil.AssertEquals(t, bFoundRecord, true) @@ -391,8 +391,8 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) { queryResult2, err = itr.Next() testutil.AssertNoError(t, err, "") testutil.AssertNotNil(t, queryResult2) - versionedQueryRecord = queryResult2.(*statedb.VersionedQueryRecord) - stringRecord = string(versionedQueryRecord.Record) + versionedQueryRecord = queryResult2.(*statedb.VersionedKV) + stringRecord = string(versionedQueryRecord.Value) bFoundRecord = strings.Contains(stringRecord, "green") testutil.AssertEquals(t, bFoundRecord, true) @@ -409,8 +409,8 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) { queryResult1, err = itr.Next() testutil.AssertNoError(t, err, "") testutil.AssertNotNil(t, queryResult1) - versionedQueryRecord = queryResult1.(*statedb.VersionedQueryRecord) - stringRecord = string(versionedQueryRecord.Record) + versionedQueryRecord = queryResult1.(*statedb.VersionedKV) + stringRecord = string(versionedQueryRecord.Value) bFoundRecord = strings.Contains(stringRecord, "green") testutil.AssertEquals(t, bFoundRecord, true) @@ -418,8 +418,8 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) { queryResult2, err = itr.Next() testutil.AssertNoError(t, err, "") testutil.AssertNotNil(t, queryResult2) - versionedQueryRecord = queryResult2.(*statedb.VersionedQueryRecord) - stringRecord = string(versionedQueryRecord.Record) + versionedQueryRecord = queryResult2.(*statedb.VersionedKV) + stringRecord = string(versionedQueryRecord.Value) bFoundRecord = strings.Contains(stringRecord, "green") testutil.AssertEquals(t, bFoundRecord, true) @@ -446,8 +446,8 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) { queryResult1, err = itr.Next() testutil.AssertNoError(t, err, "") testutil.AssertNotNil(t, queryResult1) - versionedQueryRecord = queryResult1.(*statedb.VersionedQueryRecord) - stringRecord = string(versionedQueryRecord.Record) + versionedQueryRecord = queryResult1.(*statedb.VersionedKV) + stringRecord = string(versionedQueryRecord.Value) bFoundRecord = strings.Contains(stringRecord, "joe") testutil.AssertEquals(t, bFoundRecord, true) bFoundRecord = strings.Contains(stringRecord, "1000007") diff --git a/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb.go b/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb.go index f7764c9dc1a..8c2edbc0ce2 100644 --- a/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb.go +++ b/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb.go @@ -475,11 +475,9 @@ func (scanner *queryScanner) Next() (statedb.QueryResult, error) { //remove the data wrapper and return the value and version returnValue, returnVersion := removeDataWrapper(selectedResultRecord.Value, selectedResultRecord.Attachments) - return &statedb.VersionedQueryRecord{ - Namespace: namespace, - Key: key, - Version: &returnVersion, - Record: returnValue}, nil + return &statedb.VersionedKV{ + CompositeKey: statedb.CompositeKey{Namespace: namespace, Key: key}, + VersionedValue: statedb.VersionedValue{Value: returnValue, Version: &returnVersion}}, nil } func (scanner *queryScanner) Close() { diff --git a/core/ledger/kvledger/txmgmt/statedb/statedb.go b/core/ledger/kvledger/txmgmt/statedb/statedb.go index 42973f63962..8641df83416 100644 --- a/core/ledger/kvledger/txmgmt/statedb/statedb.go +++ b/core/ledger/kvledger/txmgmt/statedb/statedb.go @@ -75,14 +75,6 @@ type VersionedKV struct { VersionedValue } -// VersionedQueryRecord encloses a query record -type VersionedQueryRecord struct { - Namespace string - Key string - Version *version.Height - Record []byte -} - // ResultsIterator hepls in iterates over query results type ResultsIterator interface { Next() (QueryResult, error) diff --git a/core/ledger/kvledger/txmgmt/txmgr/commontests/txmgr_test.go b/core/ledger/kvledger/txmgmt/txmgr/commontests/txmgr_test.go index 01aa90b4f09..dfe9f38da57 100644 --- a/core/ledger/kvledger/txmgmt/txmgr/commontests/txmgr_test.go +++ b/core/ledger/kvledger/txmgmt/txmgr/commontests/txmgr_test.go @@ -591,7 +591,7 @@ func testExecuteQuery(t *testing.T, env testEnv) { //Unmarshal the document to Asset structure assetResp := &Asset{} - json.Unmarshal(queryRecord.(*ledger.QueryRecord).Record, &assetResp) + json.Unmarshal(queryRecord.(*ledger.KV).Value, &assetResp) //Verify the owner retrieved matches testutil.AssertEquals(t, assetResp.Owner, "bob") diff --git a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper.go b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper.go index 3b9d59b670f..434a76df649 100644 --- a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper.go +++ b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper.go @@ -216,13 +216,13 @@ func (itr *queryResultsItr) Next() (commonledger.QueryResult, error) { if queryResult == nil { return nil, nil } - versionedQueryRecord := queryResult.(*statedb.VersionedQueryRecord) - logger.Debugf("queryResultsItr.Next() returned a record:%s", string(versionedQueryRecord.Record)) + versionedQueryRecord := queryResult.(*statedb.VersionedKV) + logger.Debugf("queryResultsItr.Next() returned a record:%s", string(versionedQueryRecord.Value)) if itr.RWSetBuilder != nil { itr.RWSetBuilder.AddToReadSet(versionedQueryRecord.Namespace, versionedQueryRecord.Key, versionedQueryRecord.Version) } - return &ledger.QueryRecord{Namespace: versionedQueryRecord.Namespace, Key: versionedQueryRecord.Key, Record: versionedQueryRecord.Record}, nil + return &ledger.KV{Key: versionedQueryRecord.Key, Value: versionedQueryRecord.Value}, nil } // Close implements method in interface ledger.ResultsIterator diff --git a/core/ledger/ledger_interface.go b/core/ledger/ledger_interface.go index aafb7d7a09f..38ca04b599a 100644 --- a/core/ledger/ledger_interface.go +++ b/core/ledger/ledger_interface.go @@ -17,6 +17,7 @@ limitations under the License. package ledger import ( + google_protobuf "github.com/golang/protobuf/ptypes/timestamp" commonledger "github.com/hyperledger/fabric/common/ledger" "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/peer" @@ -132,14 +133,8 @@ type KV struct { // KeyModification - QueryResult for History. type KeyModification struct { - TxID string - Value []byte -} - -// QueryRecord - Result structure for query records. Holds a namespace, key and record. -// Only used for state databases that support query -type QueryRecord struct { - Namespace string - Key string - Record []byte + TxID string + Value []byte + Timestamp *google_protobuf.Timestamp + IsDelete bool } diff --git a/core/scc/lccc/lccc.go b/core/scc/lccc/lccc.go index b13f616485d..f4d7027d211 100644 --- a/core/scc/lccc/lccc.go +++ b/core/scc/lccc/lccc.go @@ -278,13 +278,13 @@ func (lccc *LifeCycleSysCC) getChaincodes(stub shim.ChaincodeStubInterface) pb.R var ccInfoArray []*pb.ChaincodeInfo for itr.HasNext() { - _, value, err := itr.Next() + response, err := itr.Next() if err != nil { return shim.Error(err.Error()) } ccdata := &ccprovider.ChaincodeData{} - if err = proto.Unmarshal(value, ccdata); err != nil { + if err = proto.Unmarshal(response.Value, ccdata); err != nil { return shim.Error(err.Error()) } diff --git a/examples/chaincode/go/map/map.go b/examples/chaincode/go/map/map.go index f77c57e6ddd..f02c0ddab6a 100644 --- a/examples/chaincode/go/map/map.go +++ b/examples/chaincode/go/map/map.go @@ -100,11 +100,15 @@ func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { var keys []string for keysIter.HasNext() { - key, _, iterErr := keysIter.Next() + response, iterErr := keysIter.Next() if iterErr != nil { return shim.Error(fmt.Sprintf("keys operation failed. Error accessing state: %s", err)) } - keys = append(keys, key) + keys = append(keys, response.Key) + } + + for key, value := range keys { + fmt.Printf("key %d contains %s\n", key, value) } jsonKeys, err := json.Marshal(keys) @@ -123,11 +127,38 @@ func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { var keys []string for keysIter.HasNext() { - key, _, iterErr := keysIter.Next() + response, iterErr := keysIter.Next() if iterErr != nil { return shim.Error(fmt.Sprintf("query operation failed. Error accessing state: %s", err)) } - keys = append(keys, key) + keys = append(keys, response.Key) + } + + jsonKeys, err := json.Marshal(keys) + if err != nil { + return shim.Error(fmt.Sprintf("query operation failed. Error marshaling JSON: %s", err)) + } + + return shim.Success(jsonKeys) + case "history": + key := args[0] + keysIter, err := stub.GetHistoryForKey(key) + if err != nil { + return shim.Error(fmt.Sprintf("query operation failed. Error accessing state: %s", err)) + } + defer keysIter.Close() + + var keys []string + for keysIter.HasNext() { + response, iterErr := keysIter.Next() + if iterErr != nil { + return shim.Error(fmt.Sprintf("query operation failed. Error accessing state: %s", err)) + } + keys = append(keys, response.TxID) + } + + for key, txID := range keys { + fmt.Printf("key %d contains %s\n", key, txID) } jsonKeys, err := json.Marshal(keys) diff --git a/examples/chaincode/go/marbles02/marbles_chaincode.go b/examples/chaincode/go/marbles02/marbles_chaincode.go index 24f657d3057..dcb26cd3fcd 100644 --- a/examples/chaincode/go/marbles02/marbles_chaincode.go +++ b/examples/chaincode/go/marbles02/marbles_chaincode.go @@ -81,6 +81,7 @@ import ( "strconv" "strings" + "github.com/golang/protobuf/ptypes" "github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer" ) @@ -366,7 +367,7 @@ func (t *SimpleChaincode) getMarblesByRange(stub shim.ChaincodeStubInterface, ar bArrayMemberAlreadyWritten := false for resultsIterator.HasNext() { - queryResultKey, queryResultValue, err := resultsIterator.Next() + queryResponse, err := resultsIterator.Next() if err != nil { return shim.Error(err.Error()) } @@ -376,12 +377,12 @@ func (t *SimpleChaincode) getMarblesByRange(stub shim.ChaincodeStubInterface, ar } buffer.WriteString("{\"Key\":") buffer.WriteString("\"") - buffer.WriteString(queryResultKey) + buffer.WriteString(queryResponse.Key) buffer.WriteString("\"") buffer.WriteString(", \"Record\":") // Record is a JSON object, so we write as-is - buffer.WriteString(string(queryResultValue)) + buffer.WriteString(string(queryResponse.Value)) buffer.WriteString("}") bArrayMemberAlreadyWritten = true } @@ -424,13 +425,13 @@ func (t *SimpleChaincode) transferMarblesBasedOnColor(stub shim.ChaincodeStubInt var i int for i = 0; coloredMarbleResultsIterator.HasNext(); i++ { // Note that we don't get the value (2nd return variable), we'll just get the marble name from the composite key - colorNameKey, _, err := coloredMarbleResultsIterator.Next() + responseRange, err := coloredMarbleResultsIterator.Next() if err != nil { return shim.Error(err.Error()) } // get the color and name from color~name composite key - objectType, compositeKeyParts, err := stub.SplitCompositeKey(colorNameKey) + objectType, compositeKeyParts, err := stub.SplitCompositeKey(responseRange.Key) if err != nil { return shim.Error(err.Error()) } @@ -534,7 +535,7 @@ func getQueryResultForQueryString(stub shim.ChaincodeStubInterface, queryString bArrayMemberAlreadyWritten := false for resultsIterator.HasNext() { - queryResultKey, queryResultRecord, err := resultsIterator.Next() + queryResponse, err := resultsIterator.Next() if err != nil { return nil, err } @@ -544,12 +545,12 @@ func getQueryResultForQueryString(stub shim.ChaincodeStubInterface, queryString } buffer.WriteString("{\"Key\":") buffer.WriteString("\"") - buffer.WriteString(queryResultKey) + buffer.WriteString(queryResponse.Key) buffer.WriteString("\"") buffer.WriteString(", \"Record\":") // Record is a JSON object, so we write as-is - buffer.WriteString(string(queryResultRecord)) + buffer.WriteString(string(queryResponse.Value)) buffer.WriteString("}") bArrayMemberAlreadyWritten = true } @@ -582,7 +583,7 @@ func (t *SimpleChaincode) getHistoryForMarble(stub shim.ChaincodeStubInterface, bArrayMemberAlreadyWritten := false for resultsIterator.HasNext() { - txID, historicValue, err := resultsIterator.Next() + response, err := resultsIterator.Next() if err != nil { return shim.Error(err.Error()) } @@ -592,12 +593,29 @@ func (t *SimpleChaincode) getHistoryForMarble(stub shim.ChaincodeStubInterface, } buffer.WriteString("{\"TxId\":") buffer.WriteString("\"") - buffer.WriteString(txID) + buffer.WriteString(response.TxID) buffer.WriteString("\"") buffer.WriteString(", \"Value\":") - // historicValue is a JSON marble, so we write as-is - buffer.WriteString(string(historicValue)) + // if it was a delete operation on given key, then we need to set the + //corresponding value null. Else, we will write the response.Value + //as-is (as the Value itself a JSON marble) + if response.IsDelete { + buffer.WriteString("null") + } else { + buffer.WriteString(string(response.Value)) + } + + buffer.WriteString(", \"Timestamp\":") + buffer.WriteString("\"") + buffer.WriteString(ptypes.TimestampString(response.Timestamp)) + buffer.WriteString("\"") + + buffer.WriteString(", \"IsDelete\":") + buffer.WriteString("\"") + buffer.WriteString(strconv.FormatBool(response.IsDelete)) + buffer.WriteString("\"") + buffer.WriteString("}") bArrayMemberAlreadyWritten = true } diff --git a/protos/peer/chaincode_shim.pb.go b/protos/peer/chaincode_shim.pb.go index db931a1ef4b..1eb64339608 100644 --- a/protos/peer/chaincode_shim.pb.go +++ b/protos/peer/chaincode_shim.pb.go @@ -185,30 +185,29 @@ func (m *QueryStateClose) String() string { return proto.CompactTextS func (*QueryStateClose) ProtoMessage() {} func (*QueryStateClose) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{6} } -type QueryStateKeyValue struct { - Key string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` - Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +type QueryResultBytes struct { + ResultBytes []byte `protobuf:"bytes,1,opt,name=resultBytes,proto3" json:"resultBytes,omitempty"` } -func (m *QueryStateKeyValue) Reset() { *m = QueryStateKeyValue{} } -func (m *QueryStateKeyValue) String() string { return proto.CompactTextString(m) } -func (*QueryStateKeyValue) ProtoMessage() {} -func (*QueryStateKeyValue) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{7} } +func (m *QueryResultBytes) Reset() { *m = QueryResultBytes{} } +func (m *QueryResultBytes) String() string { return proto.CompactTextString(m) } +func (*QueryResultBytes) ProtoMessage() {} +func (*QueryResultBytes) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{7} } -type QueryStateResponse struct { - KeysAndValues []*QueryStateKeyValue `protobuf:"bytes,1,rep,name=keys_and_values,json=keysAndValues" json:"keys_and_values,omitempty"` - HasMore bool `protobuf:"varint,2,opt,name=has_more,json=hasMore" json:"has_more,omitempty"` - Id string `protobuf:"bytes,3,opt,name=id" json:"id,omitempty"` +type QueryResponse struct { + Results []*QueryResultBytes `protobuf:"bytes,1,rep,name=results" json:"results,omitempty"` + HasMore bool `protobuf:"varint,2,opt,name=has_more,json=hasMore" json:"has_more,omitempty"` + Id string `protobuf:"bytes,3,opt,name=id" json:"id,omitempty"` } -func (m *QueryStateResponse) Reset() { *m = QueryStateResponse{} } -func (m *QueryStateResponse) String() string { return proto.CompactTextString(m) } -func (*QueryStateResponse) ProtoMessage() {} -func (*QueryStateResponse) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{8} } +func (m *QueryResponse) Reset() { *m = QueryResponse{} } +func (m *QueryResponse) String() string { return proto.CompactTextString(m) } +func (*QueryResponse) ProtoMessage() {} +func (*QueryResponse) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{8} } -func (m *QueryStateResponse) GetKeysAndValues() []*QueryStateKeyValue { +func (m *QueryResponse) GetResults() []*QueryResultBytes { if m != nil { - return m.KeysAndValues + return m.Results } return nil } @@ -221,8 +220,8 @@ func init() { proto.RegisterType((*GetHistoryForKey)(nil), "protos.GetHistoryForKey") proto.RegisterType((*QueryStateNext)(nil), "protos.QueryStateNext") proto.RegisterType((*QueryStateClose)(nil), "protos.QueryStateClose") - proto.RegisterType((*QueryStateKeyValue)(nil), "protos.QueryStateKeyValue") - proto.RegisterType((*QueryStateResponse)(nil), "protos.QueryStateResponse") + proto.RegisterType((*QueryResultBytes)(nil), "protos.QueryResultBytes") + proto.RegisterType((*QueryResponse)(nil), "protos.QueryResponse") proto.RegisterEnum("protos.ChaincodeMessage_Type", ChaincodeMessage_Type_name, ChaincodeMessage_Type_value) } @@ -333,54 +332,53 @@ var _ChaincodeSupport_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("peer/chaincode_shim.proto", fileDescriptor3) } var fileDescriptor3 = []byte{ - // 779 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x94, 0xdd, 0x6e, 0xe2, 0x46, - 0x14, 0xc7, 0xd7, 0x40, 0x12, 0x38, 0x49, 0x60, 0x76, 0xb2, 0x4d, 0xbd, 0x48, 0x55, 0xa9, 0x55, - 0x55, 0x54, 0xaa, 0x4c, 0x9b, 0x4a, 0x55, 0x2f, 0x2a, 0x55, 0x04, 0x26, 0xc4, 0x82, 0xd8, 0xec, - 0xd8, 0x89, 0x96, 0xde, 0x58, 0x0e, 0x9e, 0x18, 0x6b, 0xc1, 0xe3, 0x7a, 0x86, 0xd5, 0xfa, 0xba, - 0x0f, 0xd7, 0x37, 0xe9, 0x73, 0x54, 0x63, 0x63, 0x92, 0x26, 0x5a, 0xa9, 0x57, 0x9e, 0xff, 0x39, - 0xbf, 0xf3, 0x35, 0x1a, 0x1f, 0x78, 0x9b, 0x32, 0x96, 0x0d, 0x96, 0xab, 0x20, 0x4e, 0x96, 0x3c, - 0x64, 0xbe, 0x58, 0xc5, 0x1b, 0x33, 0xcd, 0xb8, 0xe4, 0xf8, 0xb0, 0xf8, 0x88, 0x6e, 0xf7, 0x19, - 0xc2, 0x3e, 0xb2, 0x44, 0x96, 0x4c, 0xf7, 0xac, 0xf0, 0xa5, 0x19, 0x4f, 0xb9, 0x08, 0xd6, 0x3b, - 0xe3, 0xd7, 0x11, 0xe7, 0xd1, 0x9a, 0x0d, 0x0a, 0x75, 0xbf, 0x7d, 0x18, 0xc8, 0x78, 0xc3, 0x84, - 0x0c, 0x36, 0x69, 0x09, 0x18, 0xff, 0x34, 0x00, 0x8d, 0xaa, 0x7c, 0x37, 0x4c, 0x88, 0x20, 0x62, - 0xf8, 0x27, 0x68, 0xc8, 0x3c, 0x65, 0xba, 0xd6, 0xd3, 0xfa, 0xed, 0x8b, 0xaf, 0x4a, 0x54, 0x98, - 0xcf, 0x39, 0xd3, 0xcb, 0x53, 0x46, 0x0b, 0x14, 0xff, 0x0a, 0xad, 0x7d, 0x6a, 0xbd, 0xd6, 0xd3, - 0xfa, 0xc7, 0x17, 0x5d, 0xb3, 0x2c, 0x6e, 0x56, 0xc5, 0x4d, 0xaf, 0x22, 0xe8, 0x23, 0x8c, 0x75, - 0x38, 0x4a, 0x83, 0x7c, 0xcd, 0x83, 0x50, 0xaf, 0xf7, 0xb4, 0xfe, 0x09, 0xad, 0x24, 0xc6, 0xd0, - 0x90, 0x9f, 0xe2, 0x50, 0x6f, 0xf4, 0xb4, 0x7e, 0x8b, 0x16, 0x67, 0xfc, 0x03, 0x34, 0xab, 0x11, - 0xf5, 0x83, 0xa2, 0x0c, 0xaa, 0xda, 0x9b, 0xef, 0xec, 0x74, 0x4f, 0xe0, 0xdf, 0xa1, 0xf3, 0xec, - 0xb2, 0xf4, 0xc3, 0x22, 0xe8, 0xfc, 0xc5, 0x4c, 0x44, 0x79, 0x69, 0x7b, 0xf9, 0x1f, 0x6d, 0xfc, - 0x5d, 0x83, 0x86, 0x9a, 0x12, 0x9f, 0x42, 0xeb, 0xd6, 0x1e, 0x93, 0x2b, 0xcb, 0x26, 0x63, 0xf4, - 0x0a, 0x9f, 0x40, 0x93, 0x92, 0x89, 0xe5, 0x7a, 0x84, 0x22, 0x0d, 0xb7, 0x01, 0x2a, 0x45, 0xc6, - 0xa8, 0x86, 0x9b, 0xd0, 0xb0, 0x6c, 0xcb, 0x43, 0x75, 0xdc, 0x82, 0x03, 0x4a, 0x86, 0xe3, 0x05, - 0x6a, 0xe0, 0x0e, 0x1c, 0x7b, 0x74, 0x68, 0xbb, 0xc3, 0x91, 0x67, 0x39, 0x36, 0x3a, 0x50, 0x29, - 0x47, 0xce, 0xcd, 0x7c, 0x46, 0x3c, 0x32, 0x46, 0x87, 0x0a, 0x25, 0x94, 0x3a, 0x14, 0x1d, 0x29, - 0xcf, 0x84, 0x78, 0xbe, 0xeb, 0x0d, 0x3d, 0x82, 0x9a, 0x4a, 0xce, 0x6f, 0x2b, 0xd9, 0x52, 0x72, - 0x4c, 0x66, 0x3b, 0x09, 0xf8, 0x0d, 0x20, 0xcb, 0xbe, 0x73, 0xa6, 0xc4, 0x1f, 0x5d, 0x0f, 0x2d, - 0x7b, 0xe4, 0x8c, 0x09, 0x3a, 0x2e, 0x1b, 0x74, 0xe7, 0x8e, 0xed, 0x12, 0x74, 0x8a, 0xcf, 0x01, - 0xef, 0x13, 0xfa, 0x97, 0x0b, 0x9f, 0x0e, 0xed, 0x09, 0x41, 0x6d, 0x15, 0xab, 0xec, 0xef, 0x6e, - 0x09, 0x5d, 0xf8, 0x94, 0xb8, 0xb7, 0x33, 0x0f, 0x75, 0x94, 0xb5, 0xb4, 0x94, 0xbc, 0x4d, 0xde, - 0x7b, 0x08, 0xe1, 0x2f, 0xe0, 0xf5, 0x53, 0xeb, 0x68, 0xe6, 0xb8, 0x04, 0xbd, 0x56, 0xdd, 0x4c, - 0x09, 0x99, 0x0f, 0x67, 0xd6, 0x1d, 0x41, 0x18, 0x7f, 0x09, 0x67, 0x2a, 0xe3, 0xb5, 0xe5, 0x7a, - 0x0e, 0x5d, 0xf8, 0x57, 0x0e, 0xf5, 0xa7, 0x64, 0x81, 0xce, 0x8c, 0x5f, 0xe0, 0x64, 0xbe, 0x95, - 0xae, 0x0c, 0x24, 0xb3, 0x92, 0x07, 0x8e, 0x11, 0xd4, 0x3f, 0xb0, 0xbc, 0x78, 0x62, 0x2d, 0xaa, - 0x8e, 0xf8, 0x0d, 0x1c, 0x7c, 0x0c, 0xd6, 0x5b, 0x56, 0x3c, 0x9f, 0x13, 0x5a, 0x0a, 0x83, 0x40, - 0x67, 0xc2, 0xca, 0xb8, 0xcb, 0x9c, 0x06, 0x49, 0xc4, 0x70, 0x17, 0x9a, 0x42, 0x06, 0x99, 0x9c, - 0xee, 0xe3, 0xf7, 0x1a, 0x9f, 0xc3, 0x21, 0x4b, 0x42, 0xe5, 0xa9, 0x15, 0x9e, 0x9d, 0x32, 0xbe, - 0x83, 0xf6, 0x84, 0xc9, 0x77, 0x5b, 0x96, 0xe5, 0x94, 0x89, 0xed, 0x5a, 0xaa, 0x72, 0x7f, 0x2a, - 0xb9, 0x4b, 0x51, 0x0a, 0xe3, 0x5b, 0x40, 0x13, 0x26, 0xaf, 0x63, 0x21, 0x79, 0x96, 0x5f, 0xf1, - 0x4c, 0xe5, 0x7c, 0xd1, 0xaa, 0xd1, 0x83, 0x76, 0x91, 0xaa, 0x68, 0xcb, 0x66, 0x9f, 0x24, 0x6e, - 0x43, 0x2d, 0x0e, 0x77, 0x48, 0x2d, 0x0e, 0x8d, 0x6f, 0xa0, 0xf3, 0x48, 0x8c, 0xd6, 0x5c, 0xb0, - 0x17, 0xc8, 0x6f, 0x80, 0x1f, 0x91, 0x29, 0xcb, 0xef, 0xd4, 0xbc, 0xff, 0xfb, 0x5e, 0xfe, 0xd2, - 0x9e, 0x86, 0x53, 0x26, 0x52, 0x9e, 0x08, 0x86, 0x2f, 0xa1, 0xf3, 0x81, 0xe5, 0xc2, 0x0f, 0x92, - 0xd0, 0x2f, 0x40, 0xa1, 0x6b, 0xbd, 0x7a, 0xf1, 0x37, 0xee, 0x5e, 0xfc, 0xcb, 0x9a, 0xf4, 0x54, - 0x85, 0x0c, 0x93, 0xb0, 0x50, 0x02, 0xbf, 0x85, 0xe6, 0x2a, 0x10, 0xfe, 0x86, 0x67, 0x65, 0xcd, - 0x26, 0x3d, 0x5a, 0x05, 0xe2, 0x86, 0x67, 0xd5, 0x0c, 0xf5, 0x6a, 0x86, 0x8b, 0xf7, 0x4f, 0xb6, - 0x87, 0xbb, 0x4d, 0x53, 0x9e, 0x49, 0x3c, 0x86, 0x26, 0x65, 0x51, 0x2c, 0x24, 0xcb, 0xb0, 0xfe, - 0xb9, 0xdd, 0xd1, 0xfd, 0xac, 0xc7, 0x78, 0xd5, 0xd7, 0x7e, 0xd4, 0x2e, 0x1d, 0x30, 0x78, 0x16, - 0x99, 0xab, 0x3c, 0x65, 0xd9, 0x9a, 0x85, 0x11, 0xcb, 0xcc, 0x87, 0xe0, 0x3e, 0x8b, 0x97, 0x55, - 0x9c, 0x5a, 0x77, 0x7f, 0x7c, 0x1f, 0xc5, 0x72, 0xb5, 0xbd, 0x37, 0x97, 0x7c, 0x33, 0x78, 0x82, - 0x0e, 0x4a, 0xb4, 0x5c, 0x7b, 0x62, 0xa0, 0xd0, 0xfb, 0x72, 0x87, 0xfe, 0xfc, 0x6f, 0x00, 0x00, - 0x00, 0xff, 0xff, 0xf8, 0xf5, 0x06, 0x38, 0x67, 0x05, 0x00, 0x00, + // 766 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x94, 0x6f, 0x6f, 0xe2, 0x46, + 0x10, 0xc6, 0x8f, 0x3f, 0x49, 0x60, 0x20, 0xb0, 0xb7, 0xb9, 0xa6, 0x3e, 0xa4, 0xaa, 0xd4, 0xaa, + 0x2a, 0x2a, 0x55, 0xd0, 0xd2, 0xaa, 0xea, 0xbb, 0x8a, 0xe0, 0x0d, 0xb1, 0x42, 0x6c, 0x6e, 0xed, + 0x9c, 0x8e, 0xbe, 0xb1, 0x1c, 0xd8, 0x18, 0xab, 0xc0, 0xba, 0xbb, 0xcb, 0xe9, 0xfc, 0x09, 0xfb, + 0x4d, 0xfa, 0x39, 0xaa, 0xb5, 0x31, 0xe1, 0x2e, 0xca, 0x2b, 0xfb, 0x99, 0xf9, 0xcd, 0x33, 0xb3, + 0xab, 0xd5, 0x80, 0x91, 0x30, 0x26, 0x06, 0x8b, 0x55, 0x18, 0x6f, 0x17, 0x7c, 0xc9, 0xe4, 0x2a, + 0xde, 0xf4, 0x13, 0xc1, 0x15, 0xc7, 0xa7, 0xd9, 0x47, 0x76, 0xde, 0x7e, 0x4e, 0xb0, 0x8f, 0x6c, + 0xab, 0x72, 0xa4, 0x73, 0x91, 0xa5, 0x12, 0xc1, 0x13, 0x2e, 0xc3, 0xf5, 0x3e, 0xf8, 0x6d, 0xc4, + 0x79, 0xb4, 0x66, 0x83, 0x4c, 0x3d, 0xec, 0x1e, 0x07, 0x2a, 0xde, 0x30, 0xa9, 0xc2, 0x4d, 0x92, + 0x03, 0xe6, 0x7f, 0x55, 0x40, 0xe3, 0xc2, 0xee, 0x8e, 0x49, 0x19, 0x46, 0x0c, 0xff, 0x02, 0x55, + 0x95, 0x26, 0xcc, 0x28, 0x75, 0x4b, 0xbd, 0xd6, 0xf0, 0x9b, 0x1c, 0x95, 0xfd, 0x2f, 0xb9, 0xbe, + 0x9f, 0x26, 0x8c, 0x66, 0x28, 0xfe, 0x03, 0xea, 0x07, 0x6b, 0xa3, 0xdc, 0x2d, 0xf5, 0x1a, 0xc3, + 0x4e, 0x3f, 0x6f, 0xde, 0x2f, 0x9a, 0xf7, 0xfd, 0x82, 0xa0, 0x4f, 0x30, 0x36, 0xe0, 0x2c, 0x09, + 0xd3, 0x35, 0x0f, 0x97, 0x46, 0xa5, 0x5b, 0xea, 0x35, 0x69, 0x21, 0x31, 0x86, 0xaa, 0xfa, 0x14, + 0x2f, 0x8d, 0x6a, 0xb7, 0xd4, 0xab, 0xd3, 0xec, 0x1f, 0xff, 0x04, 0xb5, 0xe2, 0x88, 0xc6, 0x49, + 0xd6, 0x06, 0x15, 0xe3, 0xcd, 0xf6, 0x71, 0x7a, 0x20, 0xf0, 0x9f, 0xd0, 0x3e, 0xdc, 0x55, 0x90, + 0x5d, 0x96, 0x71, 0x9a, 0x15, 0x5d, 0x3e, 0x3b, 0x13, 0xd1, 0x59, 0xda, 0x5a, 0x7c, 0xa6, 0xcd, + 0x7f, 0xcb, 0x50, 0xd5, 0xa7, 0xc4, 0xe7, 0x50, 0xbf, 0x77, 0x2c, 0x72, 0x6d, 0x3b, 0xc4, 0x42, + 0xaf, 0x70, 0x13, 0x6a, 0x94, 0x4c, 0x6c, 0xcf, 0x27, 0x14, 0x95, 0x70, 0x0b, 0xa0, 0x50, 0xc4, + 0x42, 0x65, 0x5c, 0x83, 0xaa, 0xed, 0xd8, 0x3e, 0xaa, 0xe0, 0x3a, 0x9c, 0x50, 0x32, 0xb2, 0xe6, + 0xa8, 0x8a, 0xdb, 0xd0, 0xf0, 0xe9, 0xc8, 0xf1, 0x46, 0x63, 0xdf, 0x76, 0x1d, 0x74, 0xa2, 0x2d, + 0xc7, 0xee, 0xdd, 0x6c, 0x4a, 0x7c, 0x62, 0xa1, 0x53, 0x8d, 0x12, 0x4a, 0x5d, 0x8a, 0xce, 0x74, + 0x66, 0x42, 0xfc, 0xc0, 0xf3, 0x47, 0x3e, 0x41, 0x35, 0x2d, 0x67, 0xf7, 0x85, 0xac, 0x6b, 0x69, + 0x91, 0xe9, 0x5e, 0x02, 0x7e, 0x03, 0xc8, 0x76, 0xde, 0xbb, 0xb7, 0x24, 0x18, 0xdf, 0x8c, 0x6c, + 0x67, 0xec, 0x5a, 0x04, 0x35, 0xf2, 0x01, 0xbd, 0x99, 0xeb, 0x78, 0x04, 0x9d, 0xe3, 0x4b, 0xc0, + 0x07, 0xc3, 0xe0, 0x6a, 0x1e, 0xd0, 0x91, 0x33, 0x21, 0xa8, 0xa5, 0x6b, 0x75, 0xfc, 0xdd, 0x3d, + 0xa1, 0xf3, 0x80, 0x12, 0xef, 0x7e, 0xea, 0xa3, 0xb6, 0x8e, 0xe6, 0x91, 0x9c, 0x77, 0xc8, 0x07, + 0x1f, 0x21, 0xfc, 0x15, 0xbc, 0x3e, 0x8e, 0x8e, 0xa7, 0xae, 0x47, 0xd0, 0x6b, 0x3d, 0xcd, 0x2d, + 0x21, 0xb3, 0xd1, 0xd4, 0x7e, 0x4f, 0x10, 0xc6, 0x5f, 0xc3, 0x85, 0x76, 0xbc, 0xb1, 0x3d, 0xdf, + 0xa5, 0xf3, 0xe0, 0xda, 0xa5, 0xc1, 0x2d, 0x99, 0xa3, 0x0b, 0xf3, 0x77, 0x68, 0xce, 0x76, 0xca, + 0x53, 0xa1, 0x62, 0xf6, 0xf6, 0x91, 0x63, 0x04, 0x95, 0xbf, 0x59, 0x9a, 0x3d, 0xb1, 0x3a, 0xd5, + 0xbf, 0xf8, 0x0d, 0x9c, 0x7c, 0x0c, 0xd7, 0x3b, 0x96, 0x3d, 0x9f, 0x26, 0xcd, 0x85, 0x49, 0xa0, + 0x3d, 0x61, 0x79, 0xdd, 0x55, 0x4a, 0xc3, 0x6d, 0xc4, 0x70, 0x07, 0x6a, 0x52, 0x85, 0x42, 0xdd, + 0x1e, 0xea, 0x0f, 0x1a, 0x5f, 0xc2, 0x29, 0xdb, 0x2e, 0x75, 0xa6, 0x9c, 0x65, 0xf6, 0xca, 0xfc, + 0x01, 0x5a, 0x13, 0xa6, 0xde, 0xed, 0x98, 0x48, 0x29, 0x93, 0xbb, 0xb5, 0xd2, 0xed, 0xfe, 0xd1, + 0x72, 0x6f, 0x91, 0x0b, 0xf3, 0x7b, 0x40, 0x13, 0xa6, 0x6e, 0x62, 0xa9, 0xb8, 0x48, 0xaf, 0xb9, + 0xd0, 0x9e, 0xcf, 0x46, 0x35, 0xbb, 0xd0, 0xca, 0xac, 0xb2, 0xb1, 0x1c, 0xf6, 0x49, 0xe1, 0x16, + 0x94, 0xe3, 0xe5, 0x1e, 0x29, 0xc7, 0x4b, 0xf3, 0x3b, 0x68, 0x3f, 0x11, 0xe3, 0x35, 0x97, 0xec, + 0x19, 0xf2, 0x1b, 0xa0, 0xa3, 0x79, 0xae, 0x52, 0xc5, 0x24, 0xee, 0x42, 0x43, 0x3c, 0xc9, 0x0c, + 0x6e, 0xd2, 0xe3, 0x90, 0xb9, 0x85, 0xf3, 0xa2, 0x2a, 0xe1, 0x5b, 0xc9, 0xf0, 0x10, 0xce, 0xf2, + 0xbc, 0xc6, 0x2b, 0xbd, 0xc6, 0xd0, 0x28, 0xde, 0xf6, 0x97, 0xee, 0xb4, 0x00, 0xf1, 0x5b, 0xa8, + 0xad, 0x42, 0x19, 0x6c, 0xb8, 0xc8, 0x6f, 0xbb, 0x46, 0xcf, 0x56, 0xa1, 0xbc, 0xe3, 0xa2, 0x98, + 0xb2, 0x52, 0x4c, 0x39, 0xfc, 0x70, 0xb4, 0x1f, 0xbc, 0x5d, 0x92, 0x70, 0xa1, 0xb0, 0x05, 0x35, + 0xca, 0xa2, 0x58, 0x2a, 0x26, 0xb0, 0xf1, 0xd2, 0x76, 0xe8, 0xbc, 0x98, 0x31, 0x5f, 0xf5, 0x4a, + 0x3f, 0x97, 0xae, 0xc6, 0x70, 0xc9, 0x45, 0xd4, 0x5f, 0xa5, 0x09, 0x13, 0x6b, 0xb6, 0x8c, 0x98, + 0xd8, 0x17, 0xfc, 0xf5, 0x63, 0x14, 0xab, 0xd5, 0xee, 0xa1, 0xbf, 0xe0, 0x9b, 0xc1, 0x51, 0x7a, + 0xf0, 0x18, 0x3e, 0x88, 0x78, 0x91, 0x2f, 0x33, 0x39, 0xd0, 0xfb, 0xee, 0x21, 0x5f, 0x8c, 0xbf, + 0xfe, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x1e, 0x47, 0x87, 0x0b, 0x3b, 0x05, 0x00, 0x00, } diff --git a/protos/peer/chaincode_shim.proto b/protos/peer/chaincode_shim.proto index 56215f62666..ecebcd1bb40 100644 --- a/protos/peer/chaincode_shim.proto +++ b/protos/peer/chaincode_shim.proto @@ -87,13 +87,12 @@ message QueryStateClose { string id = 1; } -message QueryStateKeyValue { - string key = 1; - bytes value = 2; +message QueryResultBytes { + bytes resultBytes = 1; } -message QueryStateResponse { - repeated QueryStateKeyValue keys_and_values = 1; +message QueryResponse { + repeated QueryResultBytes results = 1; bool has_more = 2; string id = 3; }