Skip to content

Commit 7114542

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

File tree

4 files changed

+219
-11
lines changed

4 files changed

+219
-11
lines changed

shim/handler.go

+86-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,80 @@ 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+
for i := range responses {
310+
if len(responses[i]) == 0 {
311+
responses[i] = nil
312+
}
313+
}
314+
315+
return responses, nil
316+
}
317+
318+
// handleOneSendGetMultipleStates communicates with the peer to fetch one batch of keys from the ledger.
319+
func (h *Handler) handleOneSendGetMultipleStates(collection string, keys []string, channelID string, txID string) ([][]byte, error) {
320+
// Construct payload for GET_STATE_MULTIPLE
321+
payloadBytes := marshalOrPanic(&peer.GetStateMultiple{Keys: keys, Collection: collection})
322+
323+
msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_STATE_MULTIPLE, Payload: payloadBytes, Txid: txID, ChannelId: channelID}
324+
responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txID)
325+
if err != nil {
326+
return nil, fmt.Errorf("[%s] error sending %s: %s", shorttxid(txID), peer.ChaincodeMessage_GET_STATE_MULTIPLE, err)
327+
}
328+
329+
if responseMsg.Type == peer.ChaincodeMessage_RESPONSE {
330+
// Success response
331+
var gmkResult peer.GetStateMultipleResult
332+
err = proto.Unmarshal(responseMsg.Payload, &gmkResult)
333+
if err != nil {
334+
return nil, errors.New("could not unmarshal get state multiple keys response")
335+
}
336+
337+
return gmkResult.GetValues(), nil
338+
}
339+
if responseMsg.Type == peer.ChaincodeMessage_ERROR {
340+
// Error response
341+
return nil, fmt.Errorf("%s", responseMsg.Payload[:])
342+
}
343+
344+
// Incorrect chaincode message received
345+
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)
346+
}
347+
270348
func (h *Handler) handleGetPrivateDataHash(collection string, key string, channelID string, txid string) ([]byte, error) {
271349
// Construct payload for GET_PRIVATE_DATA_HASH
272350
payloadBytes := marshalOrPanic(&peer.GetState{Collection: collection, Key: key})
@@ -733,6 +811,13 @@ func (h *Handler) handleEstablished(msg *peer.ChaincodeMessage) error {
733811
h.maxSizeWriteBatch = defaultMaxSizeWriteBatch
734812
}
735813

814+
h.usePeerGetMultipleKeys = ccAdditionalParams.UseGetMultipleKeys
815+
h.maxSizeGetMultipleKeys = ccAdditionalParams.MaxSizeGetMultipleKeys
816+
817+
if h.usePeerGetMultipleKeys && h.maxSizeGetMultipleKeys < defaultMaxSizeGetMultipleKeys {
818+
h.maxSizeGetMultipleKeys = defaultMaxSizeGetMultipleKeys
819+
}
820+
736821
return nil
737822
}
738823

shim/interfaces.go

+19
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ 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 returns values in the same order in which the keys are provided,
89+
// and any keys missing from the ledger return a nil.
90+
GetMultipleStates(keys ...string) ([][]byte, error)
91+
8392
// PutState puts the specified `key` and `value` into the transaction's
8493
// writeset as a data-write proposal. PutState doesn't effect the ledger
8594
// until the transaction is validated and successfully committed.
@@ -247,6 +256,16 @@ type ChaincodeStubInterface interface {
247256
// that has not been committed.
248257
GetPrivateData(collection, key string) ([]byte, error)
249258

259+
// GetMultiplePrivateData returns the values of the specified `keys` from the specified
260+
// `collection`. Note that GetMultiplePrivateData doesn't read data from the
261+
// private writeset, which has not been committed to the `collection`. In
262+
// other words, GetMultiplePrivateData doesn't consider data modified by PutPrivateData
263+
// that has not been committed.
264+
// If the keys do not exist in the state database, (nil, nil) is returned.
265+
// GetMultiplePrivateData returns values in the same order in which the keys are provided,
266+
// and any keys missing from the ledger return a nil.
267+
GetMultiplePrivateData(collection string, keys ...string) ([][]byte, error)
268+
250269
// GetPrivateDataHash returns the hash of the value of the specified `key` from the specified
251270
// `collection`
252271
GetPrivateDataHash(collection, key string) ([]byte, error)

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 == "" {

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)