Skip to content

Commit 5460f65

Browse files
committed
add support GetMultipleKeys
Signed-off-by: Fedor Partanskiy <[email protected]>
1 parent ccc5adb commit 5460f65

File tree

4 files changed

+209
-11
lines changed

4 files changed

+209
-11
lines changed

Diff for: shim/handler.go

+80-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ const (
1919
established state = "established" // connection established
2020
ready state = "ready" // ready for requests
2121

22-
defaultMaxSizeWriteBatch = 100
22+
defaultMaxSizeWriteBatch = 100
23+
defaultMaxSizeGetMultipleKeys = 100
2324
)
2425

2526
// PeerChaincodeStream is the common stream interface for Peer - chaincode communication.
@@ -51,6 +52,9 @@ type Handler struct {
5152
// if you can send the changes in batches.
5253
usePeerWriteBatch bool
5354
maxSizeWriteBatch uint32
55+
// if you can get the multiple keys in batches.
56+
usePeerGetMultipleKeys bool
57+
maxSizeGetMultipleKeys uint32
5458

5559
// Multiple queries (and one transaction) with different txids can be executing in parallel for this chaincode
5660
// responseChannels is the channel on which responses are communicated by the shim to the chaincodeStub.
@@ -267,6 +271,74 @@ func (h *Handler) handleGetState(collection string, key string, channelID string
267271
return nil, fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR)
268272
}
269273

274+
// handleGetMultipleStates communicates with the peer to fetch the requested state information from the ledger.
275+
func (h *Handler) handleGetMultipleStates(collection string, keys []string, channelID string, txID string) ([][]byte, error) {
276+
if len(keys) == 0 {
277+
return nil, nil
278+
}
279+
280+
responses := make([][]byte, 0, len(keys))
281+
282+
if !h.usePeerGetMultipleKeys {
283+
for _, key := range keys {
284+
resp, err := h.handleGetState(collection, key, channelID, txID)
285+
if err != nil {
286+
return nil, err
287+
}
288+
responses = append(responses, resp)
289+
}
290+
return responses, nil
291+
}
292+
293+
for ; len(keys) > int(h.maxSizeGetMultipleKeys); keys = keys[h.maxSizeGetMultipleKeys:] {
294+
resp, err := h.handleOneSendGetMultipleStates(collection, keys[:h.maxSizeGetMultipleKeys], channelID, txID)
295+
if err != nil {
296+
return nil, err
297+
}
298+
responses = append(responses, resp...)
299+
}
300+
301+
if len(keys) > 0 {
302+
resp, err := h.handleOneSendGetMultipleStates(collection, keys, channelID, txID)
303+
if err != nil {
304+
return nil, err
305+
}
306+
responses = append(responses, resp...)
307+
}
308+
309+
return responses, nil
310+
}
311+
312+
// handleOneSendGetMultipleStates communicates with the peer to fetch one batch of keys from the ledger.
313+
func (h *Handler) handleOneSendGetMultipleStates(collection string, keys []string, channelID string, txID string) ([][]byte, error) {
314+
// Construct payload for GET_STATE_MULTIPLE
315+
payloadBytes := marshalOrPanic(&peer.GetStateMultiple{Keys: keys, Collection: collection})
316+
317+
msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_STATE_MULTIPLE, Payload: payloadBytes, Txid: txID, ChannelId: channelID}
318+
responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txID)
319+
if err != nil {
320+
return nil, fmt.Errorf("[%s] error sending %s: %s", shorttxid(txID), peer.ChaincodeMessage_GET_STATE_MULTIPLE, err)
321+
}
322+
323+
if responseMsg.Type == peer.ChaincodeMessage_RESPONSE {
324+
// Success response
325+
var gmkResult peer.GetStateMultipleResult
326+
err = proto.Unmarshal(responseMsg.Payload, &gmkResult)
327+
if err != nil {
328+
return nil, errors.New("could not unmarshal get state multiple keys response")
329+
}
330+
331+
return gmkResult.GetValues(), nil
332+
}
333+
if responseMsg.Type == peer.ChaincodeMessage_ERROR {
334+
// Error response
335+
return nil, fmt.Errorf("%s", responseMsg.Payload[:])
336+
}
337+
338+
// Incorrect chaincode message received
339+
return nil, fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR)
340+
}
341+
270342
func (h *Handler) handleGetPrivateDataHash(collection string, key string, channelID string, txid string) ([]byte, error) {
271343
// Construct payload for GET_PRIVATE_DATA_HASH
272344
payloadBytes := marshalOrPanic(&peer.GetState{Collection: collection, Key: key})
@@ -733,6 +805,13 @@ func (h *Handler) handleEstablished(msg *peer.ChaincodeMessage) error {
733805
h.maxSizeWriteBatch = defaultMaxSizeWriteBatch
734806
}
735807

808+
h.usePeerGetMultipleKeys = ccAdditionalParams.UseGetMultipleKeys
809+
h.maxSizeGetMultipleKeys = ccAdditionalParams.MaxSizeGetMultipleKeys
810+
811+
if h.usePeerGetMultipleKeys && h.maxSizeGetMultipleKeys < defaultMaxSizeGetMultipleKeys {
812+
h.maxSizeGetMultipleKeys = defaultMaxSizeGetMultipleKeys
813+
}
814+
736815
return nil
737816
}
738817

Diff for: shim/interfaces.go

+15
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ type ChaincodeStubInterface interface {
8080
// If the key does not exist in the state database, (nil, nil) is returned.
8181
GetState(key string) ([]byte, error)
8282

83+
// GetMultipleStates returns the values of the specified `keys` from the
84+
// ledger. Note that GetMultipleStates doesn't read data from the writeset, which
85+
// has not been committed to the ledger. In other words, GetMultipleStates doesn't
86+
// consider data modified by PutState that has not been committed.
87+
// If the keys do not exist in the state database, (nil, nil) is returned.
88+
GetMultipleStates(keys ...string) ([][]byte, error)
89+
8390
// PutState puts the specified `key` and `value` into the transaction's
8491
// writeset as a data-write proposal. PutState doesn't effect the ledger
8592
// until the transaction is validated and successfully committed.
@@ -247,6 +254,14 @@ type ChaincodeStubInterface interface {
247254
// that has not been committed.
248255
GetPrivateData(collection, key string) ([]byte, error)
249256

257+
// GetMultiplePrivateData returns the values of the specified `keys` from the specified
258+
// `collection`. Note that GetMultiplePrivateData doesn't read data from the
259+
// private writeset, which has not been committed to the `collection`. In
260+
// other words, GetMultiplePrivateData doesn't consider data modified by PutPrivateData
261+
// that has not been committed.
262+
// If the keys do not exist in the state database, (nil, nil) is returned.
263+
GetMultiplePrivateData(collection string, keys ...string) ([][]byte, error)
264+
250265
// GetPrivateDataHash returns the hash of the value of the specified `key` from the specified
251266
// `collection`
252267
GetPrivateDataHash(collection, key string) ([]byte, error)

Diff for: shim/stub.go

+15
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@ func (s *ChaincodeStub) GetState(key string) ([]byte, error) {
166166
return s.handler.handleGetState(collection, key, s.ChannelID, s.TxID)
167167
}
168168

169+
// GetMultipleStates documentation can be found in interfaces.go
170+
func (s *ChaincodeStub) GetMultipleStates(keys ...string) ([][]byte, error) {
171+
// Access public data by setting the collection to empty string
172+
collection := ""
173+
return s.handler.handleGetMultipleStates(collection, keys, s.ChannelID, s.TxID)
174+
}
175+
169176
// SetStateValidationParameter documentation can be found in interfaces.go
170177
func (s *ChaincodeStub) SetStateValidationParameter(key string, ep []byte) error {
171178
return s.putStateMetadataEntry("", key, s.validationParameterMetakey, ep)
@@ -262,6 +269,14 @@ func (s *ChaincodeStub) GetPrivateData(collection string, key string) ([]byte, e
262269
return s.handler.handleGetState(collection, key, s.ChannelID, s.TxID)
263270
}
264271

272+
// GetMultiplePrivateData documentation can be found in interfaces.go
273+
func (s *ChaincodeStub) GetMultiplePrivateData(collection string, keys ...string) ([][]byte, error) {
274+
if collection == "" {
275+
return nil, fmt.Errorf("collection must not be an empty string")
276+
}
277+
return s.handler.handleGetMultipleStates(collection, keys, s.ChannelID, s.TxID)
278+
}
279+
265280
// GetPrivateDataHash documentation can be found in interfaces.go
266281
func (s *ChaincodeStub) GetPrivateDataHash(collection string, key string) ([]byte, error) {
267282
if collection == "" {

Diff for: shim/stub_test.go

+99-10
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,19 @@ func TestGetMSPID(t *testing.T) {
252252
}
253253

254254
func TestChaincodeStubHandlers(t *testing.T) {
255+
gmkResult := &peer.GetStateMultipleResult{
256+
Values: [][]byte{[]byte("myvalue"), []byte("myvalue")},
257+
}
258+
getMultipleKeysBytes, err := proto.Marshal(gmkResult)
259+
assert.NoError(t, err)
260+
255261
var tests = []struct {
256-
name string
257-
resType peer.ChaincodeMessage_Type
258-
payload []byte
259-
usePeerWriteBatch bool
260-
testFunc func(*ChaincodeStub, *Handler, *testing.T, []byte)
262+
name string
263+
resType peer.ChaincodeMessage_Type
264+
payload []byte
265+
usePeerWriteBatch bool
266+
usePeerGetMultipleKeys bool
267+
testFunc func(*ChaincodeStub, *Handler, *testing.T, []byte)
261268
}{
262269
{
263270
name: "Simple Response",
@@ -730,6 +737,86 @@ func TestChaincodeStubHandlers(t *testing.T) {
730737
checkWriteBatch(t, s.handler.chatStream, 1, 2)
731738
},
732739
},
740+
{
741+
name: "Get Multiple Keys - peer new",
742+
resType: peer.ChaincodeMessage_RESPONSE,
743+
payload: getMultipleKeysBytes,
744+
usePeerGetMultipleKeys: true,
745+
testFunc: func(s *ChaincodeStub, h *Handler, t *testing.T, payload []byte) {
746+
resp, err := s.GetMultipleStates("key", "key2", "key3", "key4")
747+
assert.NoError(t, err)
748+
assert.Len(t, resp, 4)
749+
for _, r := range resp {
750+
assert.Equal(t, []byte("myvalue"), r)
751+
}
752+
753+
resp, err = s.GetMultiplePrivateData("col", "key", "key2", "key3", "key4")
754+
assert.NoError(t, err)
755+
assert.Len(t, resp, 4)
756+
for _, r := range resp {
757+
assert.Equal(t, []byte("myvalue"), r)
758+
}
759+
},
760+
},
761+
{
762+
name: "Get Multiple Keys - peer old",
763+
resType: peer.ChaincodeMessage_RESPONSE,
764+
payload: []byte("myvalue"),
765+
testFunc: func(s *ChaincodeStub, h *Handler, t *testing.T, payload []byte) {
766+
resp, err := s.GetMultipleStates("key", "key2", "key3", "key4")
767+
assert.NoError(t, err)
768+
assert.Len(t, resp, 4)
769+
for _, r := range resp {
770+
assert.Equal(t, payload, r)
771+
}
772+
773+
resp, err = s.GetMultiplePrivateData("col", "key", "key2", "key3", "key4")
774+
assert.NoError(t, err)
775+
assert.Len(t, resp, 4)
776+
for _, r := range resp {
777+
assert.Equal(t, payload, r)
778+
}
779+
},
780+
},
781+
{
782+
name: "Get Multiple Keys error - peer new",
783+
resType: peer.ChaincodeMessage_ERROR,
784+
payload: []byte("error"),
785+
usePeerGetMultipleKeys: true,
786+
testFunc: func(s *ChaincodeStub, h *Handler, t *testing.T, payload []byte) {
787+
_, err := s.GetMultipleStates("key", "key2")
788+
assert.EqualError(t, err, string(payload))
789+
790+
_, err = s.GetMultiplePrivateData("col", "key", "key2")
791+
assert.EqualError(t, err, string(payload))
792+
},
793+
},
794+
{
795+
name: "Get Multiple Keys error - peer old",
796+
resType: peer.ChaincodeMessage_ERROR,
797+
payload: []byte("error"),
798+
testFunc: func(s *ChaincodeStub, h *Handler, t *testing.T, payload []byte) {
799+
_, err := s.GetMultipleStates("key", "key2")
800+
assert.EqualError(t, err, string(payload))
801+
802+
_, err = s.GetMultiplePrivateData("col", "key", "key2")
803+
assert.EqualError(t, err, string(payload))
804+
},
805+
},
806+
{
807+
name: "Get Multiple Keys - without keys",
808+
resType: peer.ChaincodeMessage_RESPONSE,
809+
payload: []byte("myvalue"),
810+
testFunc: func(s *ChaincodeStub, h *Handler, t *testing.T, payload []byte) {
811+
resp, err := s.GetMultipleStates()
812+
assert.NoError(t, err)
813+
assert.Len(t, resp, 0)
814+
815+
resp, err = s.GetMultiplePrivateData("col")
816+
assert.NoError(t, err)
817+
assert.Len(t, resp, 0)
818+
},
819+
},
733820
}
734821

735822
for _, test := range tests {
@@ -738,11 +825,13 @@ func TestChaincodeStubHandlers(t *testing.T) {
738825
t.Parallel()
739826

740827
handler := &Handler{
741-
cc: &mockChaincode{},
742-
responseChannels: map[string]chan *peer.ChaincodeMessage{},
743-
state: ready,
744-
usePeerWriteBatch: test.usePeerWriteBatch,
745-
maxSizeWriteBatch: 100,
828+
cc: &mockChaincode{},
829+
responseChannels: map[string]chan *peer.ChaincodeMessage{},
830+
state: ready,
831+
usePeerWriteBatch: test.usePeerWriteBatch,
832+
maxSizeWriteBatch: 100,
833+
usePeerGetMultipleKeys: test.usePeerGetMultipleKeys,
834+
maxSizeGetMultipleKeys: 2,
746835
}
747836
stub := &ChaincodeStub{
748837
ChannelID: "channel",

0 commit comments

Comments
 (0)