Skip to content

Commit 587d79d

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

File tree

4 files changed

+196
-11
lines changed

4 files changed

+196
-11
lines changed

shim/handler.go

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

804+
h.usePeerGetMultipleKeys = ccAdditionalParams.UseGetMultipleKeys
805+
h.maxSizeGetMultipleKeys = ccAdditionalParams.MaxSizeGetMultipleKeys
806+
807+
if h.usePeerGetMultipleKeys && h.maxSizeGetMultipleKeys < defaultMaxSizeGetMultipleKeys {
808+
h.maxSizeGetMultipleKeys = defaultMaxSizeGetMultipleKeys
809+
}
810+
736811
return nil
737812
}
738813

shim/interfaces.go

+14
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+
// GetStateMultipleKeys returns the values of the specified `keys` from the
84+
// ledger. Note that GetStateMultipleKeys doesn't read data from the writeset, which
85+
// has not been committed to the ledger. In other words, GetStateMultipleKeys 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+
GetStateMultipleKeys(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,13 @@ type ChaincodeStubInterface interface {
247254
// that has not been committed.
248255
GetPrivateData(collection, key string) ([]byte, error)
249256

257+
// GetPrivateDataMultipleKeys returns the values of the specified `keys` from the specified
258+
// `collection`. Note that GetPrivateDataMultipleKeys doesn't read data from the
259+
// private writeset, which has not been committed to the `collection`. In
260+
// other words, GetPrivateDataMultipleKeys doesn't consider data modified by PutPrivateData
261+
// that has not been committed.
262+
GetPrivateDataMultipleKeys(collection string, keys []string) ([][]byte, error)
263+
250264
// GetPrivateDataHash returns the hash of the value of the specified `key` from the specified
251265
// `collection`
252266
GetPrivateDataHash(collection, key string) ([]byte, error)

shim/stub.go

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

169+
// GetStateMultipleKeys documentation can be found in interfaces.go
170+
func (s *ChaincodeStub) GetStateMultipleKeys(keys []string) ([][]byte, error) {
171+
// Access public data by setting the collection to empty string
172+
collection := ""
173+
if len(keys) == 0 {
174+
return nil, fmt.Errorf("keys must not be an empty slice")
175+
}
176+
return s.handler.handleGetStateMultipleKeys(collection, keys, s.ChannelID, s.TxID)
177+
}
178+
169179
// SetStateValidationParameter documentation can be found in interfaces.go
170180
func (s *ChaincodeStub) SetStateValidationParameter(key string, ep []byte) error {
171181
return s.putStateMetadataEntry("", key, s.validationParameterMetakey, ep)
@@ -262,6 +272,17 @@ func (s *ChaincodeStub) GetPrivateData(collection string, key string) ([]byte, e
262272
return s.handler.handleGetState(collection, key, s.ChannelID, s.TxID)
263273
}
264274

275+
// GetPrivateDataMultipleKeys documentation can be found in interfaces.go
276+
func (s *ChaincodeStub) GetPrivateDataMultipleKeys(collection string, keys []string) ([][]byte, error) {
277+
if collection == "" {
278+
return nil, fmt.Errorf("collection must not be an empty string")
279+
}
280+
if len(keys) == 0 {
281+
return nil, fmt.Errorf("keys must not be an empty slice")
282+
}
283+
return s.handler.handleGetStateMultipleKeys(collection, keys, s.ChannelID, s.TxID)
284+
}
285+
265286
// GetPrivateDataHash documentation can be found in interfaces.go
266287
func (s *ChaincodeStub) GetPrivateDataHash(collection string, key string) ([]byte, error) {
267288
if collection == "" {

shim/stub_test.go

+85-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,72 @@ 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.GetStateMultipleKeys([]string{"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.GetPrivateDataMultipleKeys("col", []string{"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.GetStateMultipleKeys([]string{"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.GetPrivateDataMultipleKeys("col", []string{"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 - 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.GetStateMultipleKeys([]string{"key", "key2"})
788+
assert.EqualError(t, err, string(payload))
789+
790+
_, err = s.GetPrivateDataMultipleKeys("col", []string{"key", "key2"})
791+
assert.EqualError(t, err, string(payload))
792+
},
793+
},
794+
{
795+
name: "Get Multiple Keys - 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.GetStateMultipleKeys([]string{"key", "key2"})
800+
assert.EqualError(t, err, string(payload))
801+
802+
_, err = s.GetPrivateDataMultipleKeys("col", []string{"key", "key2"})
803+
assert.EqualError(t, err, string(payload))
804+
},
805+
},
733806
}
734807

735808
for _, test := range tests {
@@ -738,11 +811,13 @@ func TestChaincodeStubHandlers(t *testing.T) {
738811
t.Parallel()
739812

740813
handler := &Handler{
741-
cc: &mockChaincode{},
742-
responseChannels: map[string]chan *peer.ChaincodeMessage{},
743-
state: ready,
744-
usePeerWriteBatch: test.usePeerWriteBatch,
745-
maxSizeWriteBatch: 100,
814+
cc: &mockChaincode{},
815+
responseChannels: map[string]chan *peer.ChaincodeMessage{},
816+
state: ready,
817+
usePeerWriteBatch: test.usePeerWriteBatch,
818+
maxSizeWriteBatch: 100,
819+
usePeerGetMultipleKeys: test.usePeerGetMultipleKeys,
820+
maxSizeGetMultipleKeys: 2,
746821
}
747822
stub := &ChaincodeStub{
748823
ChannelID: "channel",

0 commit comments

Comments
 (0)