diff --git a/core/chaincode/lifecycle/mock/chaincode_stub.go b/core/chaincode/lifecycle/mock/chaincode_stub.go
index b1283fa5db9..712b8f05ac1 100644
--- a/core/chaincode/lifecycle/mock/chaincode_stub.go
+++ b/core/chaincode/lifecycle/mock/chaincode_stub.go
@@ -148,6 +148,33 @@ type ChaincodeStub struct {
 		result1 shim.HistoryQueryIteratorInterface
 		result2 error
 	}
+	GetMultiplePrivateDataStub        func(string, ...string) ([][]byte, error)
+	getMultiplePrivateDataMutex       sync.RWMutex
+	getMultiplePrivateDataArgsForCall []struct {
+		arg1 string
+		arg2 []string
+	}
+	getMultiplePrivateDataReturns struct {
+		result1 [][]byte
+		result2 error
+	}
+	getMultiplePrivateDataReturnsOnCall map[int]struct {
+		result1 [][]byte
+		result2 error
+	}
+	GetMultipleStatesStub        func(...string) ([][]byte, error)
+	getMultipleStatesMutex       sync.RWMutex
+	getMultipleStatesArgsForCall []struct {
+		arg1 []string
+	}
+	getMultipleStatesReturns struct {
+		result1 [][]byte
+		result2 error
+	}
+	getMultipleStatesReturnsOnCall map[int]struct {
+		result1 [][]byte
+		result2 error
+	}
 	GetPrivateDataStub        func(string, string) ([]byte, error)
 	getPrivateDataMutex       sync.RWMutex
 	getPrivateDataArgsForCall []struct {
@@ -1213,6 +1240,135 @@ func (fake *ChaincodeStub) GetHistoryForKeyReturnsOnCall(i int, result1 shim.His
 	}{result1, result2}
 }
 
+func (fake *ChaincodeStub) GetMultiplePrivateData(arg1 string, arg2 ...string) ([][]byte, error) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	ret, specificReturn := fake.getMultiplePrivateDataReturnsOnCall[len(fake.getMultiplePrivateDataArgsForCall)]
+	fake.getMultiplePrivateDataArgsForCall = append(fake.getMultiplePrivateDataArgsForCall, struct {
+		arg1 string
+		arg2 []string
+	}{arg1, arg2})
+	stub := fake.GetMultiplePrivateDataStub
+	fakeReturns := fake.getMultiplePrivateDataReturns
+	fake.recordInvocation("GetMultiplePrivateData", []interface{}{arg1, arg2})
+	fake.getMultiplePrivateDataMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2...)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataCallCount() int {
+	fake.getMultiplePrivateDataMutex.RLock()
+	defer fake.getMultiplePrivateDataMutex.RUnlock()
+	return len(fake.getMultiplePrivateDataArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataCalls(stub func(string, ...string) ([][]byte, error)) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	defer fake.getMultiplePrivateDataMutex.Unlock()
+	fake.GetMultiplePrivateDataStub = stub
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataArgsForCall(i int) (string, []string) {
+	fake.getMultiplePrivateDataMutex.RLock()
+	defer fake.getMultiplePrivateDataMutex.RUnlock()
+	argsForCall := fake.getMultiplePrivateDataArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataReturns(result1 [][]byte, result2 error) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	defer fake.getMultiplePrivateDataMutex.Unlock()
+	fake.GetMultiplePrivateDataStub = nil
+	fake.getMultiplePrivateDataReturns = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataReturnsOnCall(i int, result1 [][]byte, result2 error) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	defer fake.getMultiplePrivateDataMutex.Unlock()
+	fake.GetMultiplePrivateDataStub = nil
+	if fake.getMultiplePrivateDataReturnsOnCall == nil {
+		fake.getMultiplePrivateDataReturnsOnCall = make(map[int]struct {
+			result1 [][]byte
+			result2 error
+		})
+	}
+	fake.getMultiplePrivateDataReturnsOnCall[i] = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetMultipleStates(arg1 ...string) ([][]byte, error) {
+	fake.getMultipleStatesMutex.Lock()
+	ret, specificReturn := fake.getMultipleStatesReturnsOnCall[len(fake.getMultipleStatesArgsForCall)]
+	fake.getMultipleStatesArgsForCall = append(fake.getMultipleStatesArgsForCall, struct {
+		arg1 []string
+	}{arg1})
+	stub := fake.GetMultipleStatesStub
+	fakeReturns := fake.getMultipleStatesReturns
+	fake.recordInvocation("GetMultipleStates", []interface{}{arg1})
+	fake.getMultipleStatesMutex.Unlock()
+	if stub != nil {
+		return stub(arg1...)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesCallCount() int {
+	fake.getMultipleStatesMutex.RLock()
+	defer fake.getMultipleStatesMutex.RUnlock()
+	return len(fake.getMultipleStatesArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesCalls(stub func(...string) ([][]byte, error)) {
+	fake.getMultipleStatesMutex.Lock()
+	defer fake.getMultipleStatesMutex.Unlock()
+	fake.GetMultipleStatesStub = stub
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesArgsForCall(i int) []string {
+	fake.getMultipleStatesMutex.RLock()
+	defer fake.getMultipleStatesMutex.RUnlock()
+	argsForCall := fake.getMultipleStatesArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesReturns(result1 [][]byte, result2 error) {
+	fake.getMultipleStatesMutex.Lock()
+	defer fake.getMultipleStatesMutex.Unlock()
+	fake.GetMultipleStatesStub = nil
+	fake.getMultipleStatesReturns = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesReturnsOnCall(i int, result1 [][]byte, result2 error) {
+	fake.getMultipleStatesMutex.Lock()
+	defer fake.getMultipleStatesMutex.Unlock()
+	fake.GetMultipleStatesStub = nil
+	if fake.getMultipleStatesReturnsOnCall == nil {
+		fake.getMultipleStatesReturnsOnCall = make(map[int]struct {
+			result1 [][]byte
+			result2 error
+		})
+	}
+	fake.getMultipleStatesReturnsOnCall[i] = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
 func (fake *ChaincodeStub) GetPrivateData(arg1 string, arg2 string) ([]byte, error) {
 	fake.getPrivateDataMutex.Lock()
 	ret, specificReturn := fake.getPrivateDataReturnsOnCall[len(fake.getPrivateDataArgsForCall)]
@@ -3010,6 +3166,10 @@ func (fake *ChaincodeStub) Invocations() map[string][][]interface{} {
 	defer fake.getFunctionAndParametersMutex.RUnlock()
 	fake.getHistoryForKeyMutex.RLock()
 	defer fake.getHistoryForKeyMutex.RUnlock()
+	fake.getMultiplePrivateDataMutex.RLock()
+	defer fake.getMultiplePrivateDataMutex.RUnlock()
+	fake.getMultipleStatesMutex.RLock()
+	defer fake.getMultipleStatesMutex.RUnlock()
 	fake.getPrivateDataMutex.RLock()
 	defer fake.getPrivateDataMutex.RUnlock()
 	fake.getPrivateDataByPartialCompositeKeyMutex.RLock()
diff --git a/core/scc/cscc/mocks/chaincode_stub.go b/core/scc/cscc/mocks/chaincode_stub.go
index 68c1ce1d781..2498bfc29d5 100644
--- a/core/scc/cscc/mocks/chaincode_stub.go
+++ b/core/scc/cscc/mocks/chaincode_stub.go
@@ -148,6 +148,33 @@ type ChaincodeStub struct {
 		result1 shim.HistoryQueryIteratorInterface
 		result2 error
 	}
+	GetMultiplePrivateDataStub        func(string, ...string) ([][]byte, error)
+	getMultiplePrivateDataMutex       sync.RWMutex
+	getMultiplePrivateDataArgsForCall []struct {
+		arg1 string
+		arg2 []string
+	}
+	getMultiplePrivateDataReturns struct {
+		result1 [][]byte
+		result2 error
+	}
+	getMultiplePrivateDataReturnsOnCall map[int]struct {
+		result1 [][]byte
+		result2 error
+	}
+	GetMultipleStatesStub        func(...string) ([][]byte, error)
+	getMultipleStatesMutex       sync.RWMutex
+	getMultipleStatesArgsForCall []struct {
+		arg1 []string
+	}
+	getMultipleStatesReturns struct {
+		result1 [][]byte
+		result2 error
+	}
+	getMultipleStatesReturnsOnCall map[int]struct {
+		result1 [][]byte
+		result2 error
+	}
 	GetPrivateDataStub        func(string, string) ([]byte, error)
 	getPrivateDataMutex       sync.RWMutex
 	getPrivateDataArgsForCall []struct {
@@ -1213,6 +1240,135 @@ func (fake *ChaincodeStub) GetHistoryForKeyReturnsOnCall(i int, result1 shim.His
 	}{result1, result2}
 }
 
+func (fake *ChaincodeStub) GetMultiplePrivateData(arg1 string, arg2 ...string) ([][]byte, error) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	ret, specificReturn := fake.getMultiplePrivateDataReturnsOnCall[len(fake.getMultiplePrivateDataArgsForCall)]
+	fake.getMultiplePrivateDataArgsForCall = append(fake.getMultiplePrivateDataArgsForCall, struct {
+		arg1 string
+		arg2 []string
+	}{arg1, arg2})
+	stub := fake.GetMultiplePrivateDataStub
+	fakeReturns := fake.getMultiplePrivateDataReturns
+	fake.recordInvocation("GetMultiplePrivateData", []interface{}{arg1, arg2})
+	fake.getMultiplePrivateDataMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2...)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataCallCount() int {
+	fake.getMultiplePrivateDataMutex.RLock()
+	defer fake.getMultiplePrivateDataMutex.RUnlock()
+	return len(fake.getMultiplePrivateDataArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataCalls(stub func(string, ...string) ([][]byte, error)) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	defer fake.getMultiplePrivateDataMutex.Unlock()
+	fake.GetMultiplePrivateDataStub = stub
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataArgsForCall(i int) (string, []string) {
+	fake.getMultiplePrivateDataMutex.RLock()
+	defer fake.getMultiplePrivateDataMutex.RUnlock()
+	argsForCall := fake.getMultiplePrivateDataArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataReturns(result1 [][]byte, result2 error) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	defer fake.getMultiplePrivateDataMutex.Unlock()
+	fake.GetMultiplePrivateDataStub = nil
+	fake.getMultiplePrivateDataReturns = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataReturnsOnCall(i int, result1 [][]byte, result2 error) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	defer fake.getMultiplePrivateDataMutex.Unlock()
+	fake.GetMultiplePrivateDataStub = nil
+	if fake.getMultiplePrivateDataReturnsOnCall == nil {
+		fake.getMultiplePrivateDataReturnsOnCall = make(map[int]struct {
+			result1 [][]byte
+			result2 error
+		})
+	}
+	fake.getMultiplePrivateDataReturnsOnCall[i] = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetMultipleStates(arg1 ...string) ([][]byte, error) {
+	fake.getMultipleStatesMutex.Lock()
+	ret, specificReturn := fake.getMultipleStatesReturnsOnCall[len(fake.getMultipleStatesArgsForCall)]
+	fake.getMultipleStatesArgsForCall = append(fake.getMultipleStatesArgsForCall, struct {
+		arg1 []string
+	}{arg1})
+	stub := fake.GetMultipleStatesStub
+	fakeReturns := fake.getMultipleStatesReturns
+	fake.recordInvocation("GetMultipleStates", []interface{}{arg1})
+	fake.getMultipleStatesMutex.Unlock()
+	if stub != nil {
+		return stub(arg1...)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesCallCount() int {
+	fake.getMultipleStatesMutex.RLock()
+	defer fake.getMultipleStatesMutex.RUnlock()
+	return len(fake.getMultipleStatesArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesCalls(stub func(...string) ([][]byte, error)) {
+	fake.getMultipleStatesMutex.Lock()
+	defer fake.getMultipleStatesMutex.Unlock()
+	fake.GetMultipleStatesStub = stub
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesArgsForCall(i int) []string {
+	fake.getMultipleStatesMutex.RLock()
+	defer fake.getMultipleStatesMutex.RUnlock()
+	argsForCall := fake.getMultipleStatesArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesReturns(result1 [][]byte, result2 error) {
+	fake.getMultipleStatesMutex.Lock()
+	defer fake.getMultipleStatesMutex.Unlock()
+	fake.GetMultipleStatesStub = nil
+	fake.getMultipleStatesReturns = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesReturnsOnCall(i int, result1 [][]byte, result2 error) {
+	fake.getMultipleStatesMutex.Lock()
+	defer fake.getMultipleStatesMutex.Unlock()
+	fake.GetMultipleStatesStub = nil
+	if fake.getMultipleStatesReturnsOnCall == nil {
+		fake.getMultipleStatesReturnsOnCall = make(map[int]struct {
+			result1 [][]byte
+			result2 error
+		})
+	}
+	fake.getMultipleStatesReturnsOnCall[i] = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
 func (fake *ChaincodeStub) GetPrivateData(arg1 string, arg2 string) ([]byte, error) {
 	fake.getPrivateDataMutex.Lock()
 	ret, specificReturn := fake.getPrivateDataReturnsOnCall[len(fake.getPrivateDataArgsForCall)]
@@ -3010,6 +3166,10 @@ func (fake *ChaincodeStub) Invocations() map[string][][]interface{} {
 	defer fake.getFunctionAndParametersMutex.RUnlock()
 	fake.getHistoryForKeyMutex.RLock()
 	defer fake.getHistoryForKeyMutex.RUnlock()
+	fake.getMultiplePrivateDataMutex.RLock()
+	defer fake.getMultiplePrivateDataMutex.RUnlock()
+	fake.getMultipleStatesMutex.RLock()
+	defer fake.getMultipleStatesMutex.RUnlock()
 	fake.getPrivateDataMutex.RLock()
 	defer fake.getPrivateDataMutex.RUnlock()
 	fake.getPrivateDataByPartialCompositeKeyMutex.RLock()
diff --git a/core/scc/lscc/mock/chaincode_stub.go b/core/scc/lscc/mock/chaincode_stub.go
index b1283fa5db9..712b8f05ac1 100644
--- a/core/scc/lscc/mock/chaincode_stub.go
+++ b/core/scc/lscc/mock/chaincode_stub.go
@@ -148,6 +148,33 @@ type ChaincodeStub struct {
 		result1 shim.HistoryQueryIteratorInterface
 		result2 error
 	}
+	GetMultiplePrivateDataStub        func(string, ...string) ([][]byte, error)
+	getMultiplePrivateDataMutex       sync.RWMutex
+	getMultiplePrivateDataArgsForCall []struct {
+		arg1 string
+		arg2 []string
+	}
+	getMultiplePrivateDataReturns struct {
+		result1 [][]byte
+		result2 error
+	}
+	getMultiplePrivateDataReturnsOnCall map[int]struct {
+		result1 [][]byte
+		result2 error
+	}
+	GetMultipleStatesStub        func(...string) ([][]byte, error)
+	getMultipleStatesMutex       sync.RWMutex
+	getMultipleStatesArgsForCall []struct {
+		arg1 []string
+	}
+	getMultipleStatesReturns struct {
+		result1 [][]byte
+		result2 error
+	}
+	getMultipleStatesReturnsOnCall map[int]struct {
+		result1 [][]byte
+		result2 error
+	}
 	GetPrivateDataStub        func(string, string) ([]byte, error)
 	getPrivateDataMutex       sync.RWMutex
 	getPrivateDataArgsForCall []struct {
@@ -1213,6 +1240,135 @@ func (fake *ChaincodeStub) GetHistoryForKeyReturnsOnCall(i int, result1 shim.His
 	}{result1, result2}
 }
 
+func (fake *ChaincodeStub) GetMultiplePrivateData(arg1 string, arg2 ...string) ([][]byte, error) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	ret, specificReturn := fake.getMultiplePrivateDataReturnsOnCall[len(fake.getMultiplePrivateDataArgsForCall)]
+	fake.getMultiplePrivateDataArgsForCall = append(fake.getMultiplePrivateDataArgsForCall, struct {
+		arg1 string
+		arg2 []string
+	}{arg1, arg2})
+	stub := fake.GetMultiplePrivateDataStub
+	fakeReturns := fake.getMultiplePrivateDataReturns
+	fake.recordInvocation("GetMultiplePrivateData", []interface{}{arg1, arg2})
+	fake.getMultiplePrivateDataMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2...)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataCallCount() int {
+	fake.getMultiplePrivateDataMutex.RLock()
+	defer fake.getMultiplePrivateDataMutex.RUnlock()
+	return len(fake.getMultiplePrivateDataArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataCalls(stub func(string, ...string) ([][]byte, error)) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	defer fake.getMultiplePrivateDataMutex.Unlock()
+	fake.GetMultiplePrivateDataStub = stub
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataArgsForCall(i int) (string, []string) {
+	fake.getMultiplePrivateDataMutex.RLock()
+	defer fake.getMultiplePrivateDataMutex.RUnlock()
+	argsForCall := fake.getMultiplePrivateDataArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataReturns(result1 [][]byte, result2 error) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	defer fake.getMultiplePrivateDataMutex.Unlock()
+	fake.GetMultiplePrivateDataStub = nil
+	fake.getMultiplePrivateDataReturns = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataReturnsOnCall(i int, result1 [][]byte, result2 error) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	defer fake.getMultiplePrivateDataMutex.Unlock()
+	fake.GetMultiplePrivateDataStub = nil
+	if fake.getMultiplePrivateDataReturnsOnCall == nil {
+		fake.getMultiplePrivateDataReturnsOnCall = make(map[int]struct {
+			result1 [][]byte
+			result2 error
+		})
+	}
+	fake.getMultiplePrivateDataReturnsOnCall[i] = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetMultipleStates(arg1 ...string) ([][]byte, error) {
+	fake.getMultipleStatesMutex.Lock()
+	ret, specificReturn := fake.getMultipleStatesReturnsOnCall[len(fake.getMultipleStatesArgsForCall)]
+	fake.getMultipleStatesArgsForCall = append(fake.getMultipleStatesArgsForCall, struct {
+		arg1 []string
+	}{arg1})
+	stub := fake.GetMultipleStatesStub
+	fakeReturns := fake.getMultipleStatesReturns
+	fake.recordInvocation("GetMultipleStates", []interface{}{arg1})
+	fake.getMultipleStatesMutex.Unlock()
+	if stub != nil {
+		return stub(arg1...)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesCallCount() int {
+	fake.getMultipleStatesMutex.RLock()
+	defer fake.getMultipleStatesMutex.RUnlock()
+	return len(fake.getMultipleStatesArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesCalls(stub func(...string) ([][]byte, error)) {
+	fake.getMultipleStatesMutex.Lock()
+	defer fake.getMultipleStatesMutex.Unlock()
+	fake.GetMultipleStatesStub = stub
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesArgsForCall(i int) []string {
+	fake.getMultipleStatesMutex.RLock()
+	defer fake.getMultipleStatesMutex.RUnlock()
+	argsForCall := fake.getMultipleStatesArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesReturns(result1 [][]byte, result2 error) {
+	fake.getMultipleStatesMutex.Lock()
+	defer fake.getMultipleStatesMutex.Unlock()
+	fake.GetMultipleStatesStub = nil
+	fake.getMultipleStatesReturns = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesReturnsOnCall(i int, result1 [][]byte, result2 error) {
+	fake.getMultipleStatesMutex.Lock()
+	defer fake.getMultipleStatesMutex.Unlock()
+	fake.GetMultipleStatesStub = nil
+	if fake.getMultipleStatesReturnsOnCall == nil {
+		fake.getMultipleStatesReturnsOnCall = make(map[int]struct {
+			result1 [][]byte
+			result2 error
+		})
+	}
+	fake.getMultipleStatesReturnsOnCall[i] = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
 func (fake *ChaincodeStub) GetPrivateData(arg1 string, arg2 string) ([]byte, error) {
 	fake.getPrivateDataMutex.Lock()
 	ret, specificReturn := fake.getPrivateDataReturnsOnCall[len(fake.getPrivateDataArgsForCall)]
@@ -3010,6 +3166,10 @@ func (fake *ChaincodeStub) Invocations() map[string][][]interface{} {
 	defer fake.getFunctionAndParametersMutex.RUnlock()
 	fake.getHistoryForKeyMutex.RLock()
 	defer fake.getHistoryForKeyMutex.RUnlock()
+	fake.getMultiplePrivateDataMutex.RLock()
+	defer fake.getMultiplePrivateDataMutex.RUnlock()
+	fake.getMultipleStatesMutex.RLock()
+	defer fake.getMultipleStatesMutex.RUnlock()
 	fake.getPrivateDataMutex.RLock()
 	defer fake.getPrivateDataMutex.RUnlock()
 	fake.getPrivateDataByPartialCompositeKeyMutex.RLock()
diff --git a/core/scc/qscc/mocks/chaincode_stub.go b/core/scc/qscc/mocks/chaincode_stub.go
index 68c1ce1d781..2498bfc29d5 100644
--- a/core/scc/qscc/mocks/chaincode_stub.go
+++ b/core/scc/qscc/mocks/chaincode_stub.go
@@ -148,6 +148,33 @@ type ChaincodeStub struct {
 		result1 shim.HistoryQueryIteratorInterface
 		result2 error
 	}
+	GetMultiplePrivateDataStub        func(string, ...string) ([][]byte, error)
+	getMultiplePrivateDataMutex       sync.RWMutex
+	getMultiplePrivateDataArgsForCall []struct {
+		arg1 string
+		arg2 []string
+	}
+	getMultiplePrivateDataReturns struct {
+		result1 [][]byte
+		result2 error
+	}
+	getMultiplePrivateDataReturnsOnCall map[int]struct {
+		result1 [][]byte
+		result2 error
+	}
+	GetMultipleStatesStub        func(...string) ([][]byte, error)
+	getMultipleStatesMutex       sync.RWMutex
+	getMultipleStatesArgsForCall []struct {
+		arg1 []string
+	}
+	getMultipleStatesReturns struct {
+		result1 [][]byte
+		result2 error
+	}
+	getMultipleStatesReturnsOnCall map[int]struct {
+		result1 [][]byte
+		result2 error
+	}
 	GetPrivateDataStub        func(string, string) ([]byte, error)
 	getPrivateDataMutex       sync.RWMutex
 	getPrivateDataArgsForCall []struct {
@@ -1213,6 +1240,135 @@ func (fake *ChaincodeStub) GetHistoryForKeyReturnsOnCall(i int, result1 shim.His
 	}{result1, result2}
 }
 
+func (fake *ChaincodeStub) GetMultiplePrivateData(arg1 string, arg2 ...string) ([][]byte, error) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	ret, specificReturn := fake.getMultiplePrivateDataReturnsOnCall[len(fake.getMultiplePrivateDataArgsForCall)]
+	fake.getMultiplePrivateDataArgsForCall = append(fake.getMultiplePrivateDataArgsForCall, struct {
+		arg1 string
+		arg2 []string
+	}{arg1, arg2})
+	stub := fake.GetMultiplePrivateDataStub
+	fakeReturns := fake.getMultiplePrivateDataReturns
+	fake.recordInvocation("GetMultiplePrivateData", []interface{}{arg1, arg2})
+	fake.getMultiplePrivateDataMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2...)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataCallCount() int {
+	fake.getMultiplePrivateDataMutex.RLock()
+	defer fake.getMultiplePrivateDataMutex.RUnlock()
+	return len(fake.getMultiplePrivateDataArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataCalls(stub func(string, ...string) ([][]byte, error)) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	defer fake.getMultiplePrivateDataMutex.Unlock()
+	fake.GetMultiplePrivateDataStub = stub
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataArgsForCall(i int) (string, []string) {
+	fake.getMultiplePrivateDataMutex.RLock()
+	defer fake.getMultiplePrivateDataMutex.RUnlock()
+	argsForCall := fake.getMultiplePrivateDataArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataReturns(result1 [][]byte, result2 error) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	defer fake.getMultiplePrivateDataMutex.Unlock()
+	fake.GetMultiplePrivateDataStub = nil
+	fake.getMultiplePrivateDataReturns = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetMultiplePrivateDataReturnsOnCall(i int, result1 [][]byte, result2 error) {
+	fake.getMultiplePrivateDataMutex.Lock()
+	defer fake.getMultiplePrivateDataMutex.Unlock()
+	fake.GetMultiplePrivateDataStub = nil
+	if fake.getMultiplePrivateDataReturnsOnCall == nil {
+		fake.getMultiplePrivateDataReturnsOnCall = make(map[int]struct {
+			result1 [][]byte
+			result2 error
+		})
+	}
+	fake.getMultiplePrivateDataReturnsOnCall[i] = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetMultipleStates(arg1 ...string) ([][]byte, error) {
+	fake.getMultipleStatesMutex.Lock()
+	ret, specificReturn := fake.getMultipleStatesReturnsOnCall[len(fake.getMultipleStatesArgsForCall)]
+	fake.getMultipleStatesArgsForCall = append(fake.getMultipleStatesArgsForCall, struct {
+		arg1 []string
+	}{arg1})
+	stub := fake.GetMultipleStatesStub
+	fakeReturns := fake.getMultipleStatesReturns
+	fake.recordInvocation("GetMultipleStates", []interface{}{arg1})
+	fake.getMultipleStatesMutex.Unlock()
+	if stub != nil {
+		return stub(arg1...)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesCallCount() int {
+	fake.getMultipleStatesMutex.RLock()
+	defer fake.getMultipleStatesMutex.RUnlock()
+	return len(fake.getMultipleStatesArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesCalls(stub func(...string) ([][]byte, error)) {
+	fake.getMultipleStatesMutex.Lock()
+	defer fake.getMultipleStatesMutex.Unlock()
+	fake.GetMultipleStatesStub = stub
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesArgsForCall(i int) []string {
+	fake.getMultipleStatesMutex.RLock()
+	defer fake.getMultipleStatesMutex.RUnlock()
+	argsForCall := fake.getMultipleStatesArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesReturns(result1 [][]byte, result2 error) {
+	fake.getMultipleStatesMutex.Lock()
+	defer fake.getMultipleStatesMutex.Unlock()
+	fake.GetMultipleStatesStub = nil
+	fake.getMultipleStatesReturns = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetMultipleStatesReturnsOnCall(i int, result1 [][]byte, result2 error) {
+	fake.getMultipleStatesMutex.Lock()
+	defer fake.getMultipleStatesMutex.Unlock()
+	fake.GetMultipleStatesStub = nil
+	if fake.getMultipleStatesReturnsOnCall == nil {
+		fake.getMultipleStatesReturnsOnCall = make(map[int]struct {
+			result1 [][]byte
+			result2 error
+		})
+	}
+	fake.getMultipleStatesReturnsOnCall[i] = struct {
+		result1 [][]byte
+		result2 error
+	}{result1, result2}
+}
+
 func (fake *ChaincodeStub) GetPrivateData(arg1 string, arg2 string) ([]byte, error) {
 	fake.getPrivateDataMutex.Lock()
 	ret, specificReturn := fake.getPrivateDataReturnsOnCall[len(fake.getPrivateDataArgsForCall)]
@@ -3010,6 +3166,10 @@ func (fake *ChaincodeStub) Invocations() map[string][][]interface{} {
 	defer fake.getFunctionAndParametersMutex.RUnlock()
 	fake.getHistoryForKeyMutex.RLock()
 	defer fake.getHistoryForKeyMutex.RUnlock()
+	fake.getMultiplePrivateDataMutex.RLock()
+	defer fake.getMultiplePrivateDataMutex.RUnlock()
+	fake.getMultipleStatesMutex.RLock()
+	defer fake.getMultipleStatesMutex.RUnlock()
 	fake.getPrivateDataMutex.RLock()
 	defer fake.getPrivateDataMutex.RUnlock()
 	fake.getPrivateDataByPartialCompositeKeyMutex.RLock()
diff --git a/go.mod b/go.mod
index e15a3e1e407..9f3ef52417e 100644
--- a/go.mod
+++ b/go.mod
@@ -16,7 +16,7 @@ require (
 	github.com/gorilla/handlers v1.5.2
 	github.com/gorilla/mux v1.8.1
 	github.com/hyperledger-labs/SmartBFT v0.0.0-20241013183757-134292d4208a
-	github.com/hyperledger/fabric-chaincode-go/v2 v2.1.0
+	github.com/hyperledger/fabric-chaincode-go/v2 v2.2.0
 	github.com/hyperledger/fabric-config v0.3.0
 	github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5
 	github.com/hyperledger/fabric-protos-go-apiv2 v0.3.6
diff --git a/go.sum b/go.sum
index 120702a177e..b04f1ca2ba8 100644
--- a/go.sum
+++ b/go.sum
@@ -887,8 +887,8 @@ github.com/hyperledger/aries-bbs-go v0.0.0-20240528084656-761671ea73bc h1:3Ykk6M
 github.com/hyperledger/aries-bbs-go v0.0.0-20240528084656-761671ea73bc/go.mod h1:Kofn6A6WWea1ZM8Rys5aBW9dszwJ7Ywa0kyyYL0TPYw=
 github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 h1:B1Nt8hKb//KvgGRprk0h1t4lCnwhE9/ryb1WqfZbV+M=
 github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2/go.mod h1:X+DIyUsaTmalOpmpQfIvFZjKHQedrURQ5t4YqquX7lE=
-github.com/hyperledger/fabric-chaincode-go/v2 v2.1.0 h1:/TwtyM32+YF743mShDUzO7UIBiwQ861y6Pm5lrfmeyM=
-github.com/hyperledger/fabric-chaincode-go/v2 v2.1.0/go.mod h1:ukM+tmj1jVLvjn3612EhDswaD3pz7i+YTZKcZCE0Erg=
+github.com/hyperledger/fabric-chaincode-go/v2 v2.2.0 h1:n7rgo7iYD4cCC2+NlyI2DwtP+hYLwUl5UKPcJi4IrM8=
+github.com/hyperledger/fabric-chaincode-go/v2 v2.2.0/go.mod h1:c3zA3gOL/V53a0v1TGgHe8nifeH6daG/UrmJs79I9pI=
 github.com/hyperledger/fabric-config v0.3.0 h1:FS5/dc9GAniljP6RYxQRG92AaiBVoN2vTvtOvnWqeQs=
 github.com/hyperledger/fabric-config v0.3.0/go.mod h1:kSevTn78K83Suc++JsEo7Nt1tYIPqDajW+ORz3OhWlg=
 github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5 h1:RPWTL5wxAb+xDOrsCU3QYZP65305F8v3PaOyzdbPVMU=
diff --git a/vendor/github.com/hyperledger/fabric-chaincode-go/v2/shim/handler.go b/vendor/github.com/hyperledger/fabric-chaincode-go/v2/shim/handler.go
index 9c5fea72f42..346a15ac900 100644
--- a/vendor/github.com/hyperledger/fabric-chaincode-go/v2/shim/handler.go
+++ b/vendor/github.com/hyperledger/fabric-chaincode-go/v2/shim/handler.go
@@ -19,7 +19,8 @@ const (
 	established state = "established" // connection established
 	ready       state = "ready"       // ready for requests
 
-	defaultMaxSizeWriteBatch = 100
+	defaultMaxSizeWriteBatch      = 100
+	defaultMaxSizeGetMultipleKeys = 100
 )
 
 // PeerChaincodeStream is the common stream interface for Peer - chaincode communication.
@@ -51,6 +52,9 @@ type Handler struct {
 	// if you can send the changes in batches.
 	usePeerWriteBatch bool
 	maxSizeWriteBatch uint32
+	// if you can get the multiple keys in batches.
+	usePeerGetMultipleKeys bool
+	maxSizeGetMultipleKeys uint32
 
 	// Multiple queries (and one transaction) with different txids can be executing in parallel for this chaincode
 	// 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
 	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)
 }
 
+// handleGetMultipleStates communicates with the peer to fetch the requested state information from the ledger.
+func (h *Handler) handleGetMultipleStates(collection string, keys []string, channelID string, txID string) ([][]byte, error) {
+	if len(keys) == 0 {
+		return nil, nil
+	}
+
+	responses := make([][]byte, 0, len(keys))
+
+	if !h.usePeerGetMultipleKeys {
+		for _, key := range keys {
+			resp, err := h.handleGetState(collection, key, channelID, txID)
+			if err != nil {
+				return nil, err
+			}
+			responses = append(responses, resp)
+		}
+		return responses, nil
+	}
+
+	for ; len(keys) > int(h.maxSizeGetMultipleKeys); keys = keys[h.maxSizeGetMultipleKeys:] {
+		resp, err := h.handleOneSendGetMultipleStates(collection, keys[:h.maxSizeGetMultipleKeys], channelID, txID)
+		if err != nil {
+			return nil, err
+		}
+		responses = append(responses, resp...)
+	}
+
+	if len(keys) > 0 {
+		resp, err := h.handleOneSendGetMultipleStates(collection, keys, channelID, txID)
+		if err != nil {
+			return nil, err
+		}
+		responses = append(responses, resp...)
+	}
+
+	for i := range responses {
+		if len(responses[i]) == 0 {
+			responses[i] = nil
+		}
+	}
+
+	return responses, nil
+}
+
+// handleOneSendGetMultipleStates communicates with the peer to fetch one batch of keys from the ledger.
+func (h *Handler) handleOneSendGetMultipleStates(collection string, keys []string, channelID string, txID string) ([][]byte, error) {
+	// Construct payload for GET_STATE_MULTIPLE
+	payloadBytes := marshalOrPanic(&peer.GetStateMultiple{Keys: keys, Collection: collection})
+
+	msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_STATE_MULTIPLE, Payload: payloadBytes, Txid: txID, ChannelId: channelID}
+	responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txID)
+	if err != nil {
+		return nil, fmt.Errorf("[%s] error sending %s: %s", shorttxid(txID), peer.ChaincodeMessage_GET_STATE_MULTIPLE, err)
+	}
+
+	if responseMsg.Type == peer.ChaincodeMessage_RESPONSE {
+		// Success response
+		var gmkResult peer.GetStateMultipleResult
+		err = proto.Unmarshal(responseMsg.Payload, &gmkResult)
+		if err != nil {
+			return nil, errors.New("could not unmarshal get state multiple keys response")
+		}
+
+		return gmkResult.GetValues(), nil
+	}
+	if responseMsg.Type == peer.ChaincodeMessage_ERROR {
+		// Error response
+		return nil, fmt.Errorf("%s", responseMsg.Payload[:])
+	}
+
+	// Incorrect chaincode message received
+	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)
+}
+
 func (h *Handler) handleGetPrivateDataHash(collection string, key string, channelID string, txid string) ([]byte, error) {
 	// Construct payload for GET_PRIVATE_DATA_HASH
 	payloadBytes := marshalOrPanic(&peer.GetState{Collection: collection, Key: key})
@@ -733,6 +811,13 @@ func (h *Handler) handleEstablished(msg *peer.ChaincodeMessage) error {
 		h.maxSizeWriteBatch = defaultMaxSizeWriteBatch
 	}
 
+	h.usePeerGetMultipleKeys = ccAdditionalParams.UseGetMultipleKeys
+	h.maxSizeGetMultipleKeys = ccAdditionalParams.MaxSizeGetMultipleKeys
+
+	if h.usePeerGetMultipleKeys && h.maxSizeGetMultipleKeys < defaultMaxSizeGetMultipleKeys {
+		h.maxSizeGetMultipleKeys = defaultMaxSizeGetMultipleKeys
+	}
+
 	return nil
 }
 
diff --git a/vendor/github.com/hyperledger/fabric-chaincode-go/v2/shim/interfaces.go b/vendor/github.com/hyperledger/fabric-chaincode-go/v2/shim/interfaces.go
index 27f9ba19308..66993fb82e8 100644
--- a/vendor/github.com/hyperledger/fabric-chaincode-go/v2/shim/interfaces.go
+++ b/vendor/github.com/hyperledger/fabric-chaincode-go/v2/shim/interfaces.go
@@ -80,6 +80,15 @@ type ChaincodeStubInterface interface {
 	// If the key does not exist in the state database, (nil, nil) is returned.
 	GetState(key string) ([]byte, error)
 
+	// GetMultipleStates retrieves the values of the specified keys from the ledger.
+	// It has similar semantics to the GetState function regarding read-your-own-writes behavior,
+	// meaning that updates made by a previous PutState operation within the same chaincode
+	// session are not visible to this function.
+	// If no key is passed, the function will return (nil, nil).
+	// GetMultipleStates returns values in the same order in which the keys are provided,
+	// and any keys missing from the ledger return a nil.
+	GetMultipleStates(keys ...string) ([][]byte, error)
+
 	// PutState puts the specified `key` and `value` into the transaction's
 	// writeset as a data-write proposal. PutState doesn't effect the ledger
 	// until the transaction is validated and successfully committed.
@@ -247,6 +256,15 @@ type ChaincodeStubInterface interface {
 	// that has not been committed.
 	GetPrivateData(collection, key string) ([]byte, error)
 
+	// GetMultiplePrivateData retrieves the values of the specified keys from the  specified collection.
+	// It has similar semantics to the GetePrivateData function regarding read-your-own-writes behavior,
+	// meaning that updates made by a previous PutPrivateData operation within the same chaincode
+	// session are not visible to this function.
+	// If no key is passed, the function will return (nil, nil).
+	// GetMultiplePrivateData returns values in the same order in which the keys are provided,
+	// and any keys missing from the ledger return a nil.
+	GetMultiplePrivateData(collection string, keys ...string) ([][]byte, error)
+
 	// GetPrivateDataHash returns the hash of the value of the specified `key` from the specified
 	// `collection`
 	GetPrivateDataHash(collection, key string) ([]byte, error)
diff --git a/vendor/github.com/hyperledger/fabric-chaincode-go/v2/shim/stub.go b/vendor/github.com/hyperledger/fabric-chaincode-go/v2/shim/stub.go
index 6655d14fbd7..2553f2429e7 100644
--- a/vendor/github.com/hyperledger/fabric-chaincode-go/v2/shim/stub.go
+++ b/vendor/github.com/hyperledger/fabric-chaincode-go/v2/shim/stub.go
@@ -166,6 +166,13 @@ func (s *ChaincodeStub) GetState(key string) ([]byte, error) {
 	return s.handler.handleGetState(collection, key, s.ChannelID, s.TxID)
 }
 
+// GetMultipleStates documentation can be found in interfaces.go
+func (s *ChaincodeStub) GetMultipleStates(keys ...string) ([][]byte, error) {
+	// Access public data by setting the collection to empty string
+	collection := ""
+	return s.handler.handleGetMultipleStates(collection, keys, s.ChannelID, s.TxID)
+}
+
 // SetStateValidationParameter documentation can be found in interfaces.go
 func (s *ChaincodeStub) SetStateValidationParameter(key string, ep []byte) error {
 	return s.putStateMetadataEntry("", key, s.validationParameterMetakey, ep)
@@ -262,6 +269,14 @@ func (s *ChaincodeStub) GetPrivateData(collection string, key string) ([]byte, e
 	return s.handler.handleGetState(collection, key, s.ChannelID, s.TxID)
 }
 
+// GetMultiplePrivateData documentation can be found in interfaces.go
+func (s *ChaincodeStub) GetMultiplePrivateData(collection string, keys ...string) ([][]byte, error) {
+	if collection == "" {
+		return nil, fmt.Errorf("collection must not be an empty string")
+	}
+	return s.handler.handleGetMultipleStates(collection, keys, s.ChannelID, s.TxID)
+}
+
 // GetPrivateDataHash documentation can be found in interfaces.go
 func (s *ChaincodeStub) GetPrivateDataHash(collection string, key string) ([]byte, error) {
 	if collection == "" {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 5bcc8664794..8abe6a2b04c 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -216,7 +216,7 @@ github.com/hyperledger/fabric-amcl/amcl
 github.com/hyperledger/fabric-amcl/amcl/FP256BN
 github.com/hyperledger/fabric-amcl/core
 github.com/hyperledger/fabric-amcl/core/FP256BN
-# github.com/hyperledger/fabric-chaincode-go/v2 v2.1.0
+# github.com/hyperledger/fabric-chaincode-go/v2 v2.2.0
 ## explicit; go 1.22.0
 github.com/hyperledger/fabric-chaincode-go/v2/pkg/statebased
 github.com/hyperledger/fabric-chaincode-go/v2/shim