Skip to content

Commit

Permalink
add support GetMultipleKeys
Browse files Browse the repository at this point in the history
Signed-off-by: Fedor Partanskiy <[email protected]>
  • Loading branch information
pfi79 committed Feb 10, 2025
1 parent e22c269 commit 5d2b6ed
Show file tree
Hide file tree
Showing 11 changed files with 281 additions and 29 deletions.
4 changes: 4 additions & 0 deletions core/chaincode/chaincode_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ type ChaincodeSupport struct {
UserRunsCC bool
UseWriteBatch bool
MaxSizeWriteBatch uint32
UseGetMultipleKeys bool
MaxSizeGetMultipleKeys uint32
}

// Launch starts executing chaincode if it is not already running. This method
Expand Down Expand Up @@ -130,6 +132,8 @@ func (cs *ChaincodeSupport) HandleChaincodeStream(stream ccintf.ChaincodeStream)
TotalQueryLimit: cs.TotalQueryLimit,
UseWriteBatch: cs.UseWriteBatch,
MaxSizeWriteBatch: cs.MaxSizeWriteBatch,
UseGetMultipleKeys: cs.UseGetMultipleKeys,
MaxSizeGetMultipleKeys: cs.MaxSizeGetMultipleKeys,
}

return handler.ProcessStream(stream)
Expand Down
40 changes: 25 additions & 15 deletions core/chaincode/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,27 @@ import (
)

const (
defaultExecutionTimeout = 30 * time.Second
minimumStartupTimeout = 5 * time.Second
defaultMaxSizeWriteBatch = 1000
defaultExecutionTimeout = 30 * time.Second
minimumStartupTimeout = 5 * time.Second
defaultMaxSizeWriteBatch = 1000
defaultMaxSizeGetMultipleKeys = 1000
)

type Config struct {
TotalQueryLimit int
TLSEnabled bool
Keepalive time.Duration
ExecuteTimeout time.Duration
InstallTimeout time.Duration
StartupTimeout time.Duration
LogFormat string
LogLevel string
ShimLogLevel string
SCCAllowlist map[string]bool
UseWriteBatch bool
MaxSizeWriteBatch uint32
TotalQueryLimit int
TLSEnabled bool
Keepalive time.Duration
ExecuteTimeout time.Duration
InstallTimeout time.Duration
StartupTimeout time.Duration
LogFormat string
LogLevel string
ShimLogLevel string
SCCAllowlist map[string]bool
UseWriteBatch bool
MaxSizeWriteBatch uint32
UseGetMultipleKeys bool
MaxSizeGetMultipleKeys uint32
}

func GlobalConfig() *Config {
Expand Down Expand Up @@ -81,6 +84,13 @@ func (c *Config) load() {
if c.MaxSizeWriteBatch <= 0 {
c.MaxSizeWriteBatch = defaultMaxSizeWriteBatch
}
if viper.IsSet("chaincode.runtimeParams.useGetMultipleKeys") {
c.UseGetMultipleKeys = viper.GetBool("chaincode.runtimeParams.useGetMultipleKeys")
}
c.MaxSizeGetMultipleKeys = viper.GetUint32("chaincode.runtimeParams.maxSizeGetMultipleKeys")
if c.MaxSizeGetMultipleKeys <= 0 {
c.MaxSizeGetMultipleKeys = defaultMaxSizeGetMultipleKeys
}
}

func parseBool(s string) bool {
Expand Down
42 changes: 42 additions & 0 deletions core/chaincode/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ var _ = Describe("Config", func() {
viper.Set("chaincode.logging.level", "warning")
viper.Set("chaincode.logging.shim", "warning")
viper.Set("chaincode.system.somecc", true)
viper.Set("chaincode.runtimeParams.useWriteBatch", true)
viper.Set("chaincode.runtimeParams.maxSizeWriteBatch", 1001)
viper.Set("chaincode.runtimeParams.useGetMultipleKeys", true)
viper.Set("chaincode.runtimeParams.maxSizeGetMultipleKeys", 1001)

config := chaincode.GlobalConfig()
Expect(config.TLSEnabled).To(BeTrue())
Expand All @@ -48,6 +52,10 @@ var _ = Describe("Config", func() {
Expect(config.LogLevel).To(Equal("warn"))
Expect(config.ShimLogLevel).To(Equal("warn"))
Expect(config.SCCAllowlist).To(Equal(map[string]bool{"somecc": true}))
Expect(config.UseWriteBatch).To(BeTrue())
Expect(config.MaxSizeWriteBatch).To(Equal(uint32(1001)))
Expect(config.UseGetMultipleKeys).To(BeTrue())
Expect(config.MaxSizeGetMultipleKeys).To(Equal(uint32(1001)))
})

Context("when an invalid keepalive is configured", func() {
Expand Down Expand Up @@ -95,6 +103,40 @@ var _ = Describe("Config", func() {
Expect(config.ShimLogLevel).To(Equal("info"))
})
})

Context("when an runtime params is false and zero", func() {
BeforeEach(func() {
viper.Set("chaincode.runtimeParams.useWriteBatch", false)
viper.Set("chaincode.runtimeParams.maxSizeWriteBatch", 0)
viper.Set("chaincode.runtimeParams.useGetMultipleKeys", false)
viper.Set("chaincode.runtimeParams.maxSizeGetMultipleKeys", 0)
})

It("check runtime params", func() {
config := chaincode.GlobalConfig()
Expect(config.UseWriteBatch).To(BeFalse())
Expect(config.MaxSizeWriteBatch).To(Equal(uint32(1000)))
Expect(config.UseGetMultipleKeys).To(BeFalse())
Expect(config.MaxSizeGetMultipleKeys).To(Equal(uint32(1000)))
})
})

Context("when an invalid runtime params", func() {
BeforeEach(func() {
viper.Set("chaincode.runtimeParams.useWriteBatch", "abc")
viper.Set("chaincode.runtimeParams.maxSizeWriteBatch", "abc")
viper.Set("chaincode.runtimeParams.useGetMultipleKeys", "abc")
viper.Set("chaincode.runtimeParams.maxSizeGetMultipleKeys", "abc")
})

It("check runtime params", func() {
config := chaincode.GlobalConfig()
Expect(config.UseWriteBatch).To(BeFalse())
Expect(config.MaxSizeWriteBatch).To(Equal(uint32(1000)))
Expect(config.UseGetMultipleKeys).To(BeFalse())
Expect(config.MaxSizeGetMultipleKeys).To(Equal(uint32(1000)))
})
})
})

Describe("IsDevMode", func() {
Expand Down
52 changes: 50 additions & 2 deletions core/chaincode/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ type Handler struct {
UseWriteBatch bool
// MaxSizeWriteBatch maximum batch size for the change segment
MaxSizeWriteBatch uint32
// UseGetMultipleKeys an indication that the peer can handle get multiple keys
UseGetMultipleKeys bool
// MaxSizeGetMultipleKeys maximum size of batches with get multiple keys
MaxSizeGetMultipleKeys uint32

// stateLock is used to read and set State.
stateLock sync.RWMutex
Expand Down Expand Up @@ -221,6 +225,8 @@ func (h *Handler) handleMessageReadyState(msg *pb.ChaincodeMessage) error {
go h.HandleTransaction(msg, h.HandlePurgePrivateData)
case pb.ChaincodeMessage_WRITE_BATCH_STATE:
go h.HandleTransaction(msg, h.HandleWriteBatch)
case pb.ChaincodeMessage_GET_STATE_MULTIPLE:
go h.HandleTransaction(msg, h.HandleGetStateMultipleKeys)
default:
return fmt.Errorf("[%s] Fabric side handler cannot handle message (%s) while in ready state", msg.Txid, msg.Type)
}
Expand Down Expand Up @@ -449,8 +455,10 @@ func (h *Handler) sendReady() error {
chaincodeLogger.Debugf("sending READY for chaincode %s", h.chaincodeID)

chaincodeAdditionalParams := &pb.ChaincodeAdditionalParams{
UseWriteBatch: h.UseWriteBatch,
MaxSizeWriteBatch: h.MaxSizeWriteBatch,
UseWriteBatch: h.UseWriteBatch,
MaxSizeWriteBatch: h.MaxSizeWriteBatch,
UseGetMultipleKeys: h.UseGetMultipleKeys,
MaxSizeGetMultipleKeys: h.MaxSizeGetMultipleKeys,
}
payloadBytes, err := proto.Marshal(chaincodeAdditionalParams)
if err != nil {
Expand Down Expand Up @@ -678,6 +686,46 @@ func (h *Handler) HandleGetState(msg *pb.ChaincodeMessage, txContext *Transactio
return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: res, Txid: msg.Txid, ChannelId: msg.ChannelId}, nil
}

// Handles query to ledger to get state
func (h *Handler) HandleGetStateMultipleKeys(msg *pb.ChaincodeMessage, txContext *TransactionContext) (*pb.ChaincodeMessage, error) {
getState := &pb.GetStateMultiple{}
err := proto.Unmarshal(msg.Payload, getState)
if err != nil {
return nil, errors.Wrap(err, "unmarshal failed")
}

var res [][]byte
namespaceID := txContext.NamespaceID
collection := getState.GetCollection()
chaincodeLogger.Debugf("[%s] getting state for chaincode %s, keys %v, channel %s", shorttxid(msg.Txid), namespaceID, getState.GetKeys(), txContext.ChannelID)

if isCollectionSet(collection) {
if txContext.IsInitTransaction {
return nil, errors.New("private data APIs are not allowed in chaincode Init()")
}
if err = errorIfCreatorHasNoReadPermission(namespaceID, collection, txContext); err != nil {
return nil, err
}
res, err = txContext.TXSimulator.GetPrivateDataMultipleKeys(namespaceID, collection, getState.GetKeys())
} else {
res, err = txContext.TXSimulator.GetStateMultipleKeys(namespaceID, getState.GetKeys())
}
if err != nil {
return nil, errors.WithStack(err)
}
if len(res) == 0 {
chaincodeLogger.Debugf("[%s] No state associated with keys: %v. Sending %s with an empty payload", shorttxid(msg.Txid), getState.GetKeys(), pb.ChaincodeMessage_RESPONSE)
}

payloadBytes, err := proto.Marshal(&pb.GetStateMultipleResult{Values: res})
if err != nil {
return nil, errors.Wrap(err, "marshal failed")
}

// Send response msg back to chaincode. GetState will not trigger event
return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: payloadBytes, Txid: msg.Txid, ChannelId: msg.ChannelId}, nil
}

func (h *Handler) HandleGetPrivateDataHash(msg *pb.ChaincodeMessage, txContext *TransactionContext) (*pb.ChaincodeMessage, error) {
getState := &pb.GetState{}
err := proto.Unmarshal(msg.Payload, getState)
Expand Down
16 changes: 10 additions & 6 deletions core/chaincode/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,12 @@ var _ = Describe("Handler", func() {
UUIDGenerator: chaincode.UUIDGeneratorFunc(func() string {
return "generated-query-id"
}),
AppConfig: fakeApplicationConfigRetriever,
Metrics: chaincodeMetrics,
UseWriteBatch: true,
MaxSizeWriteBatch: 1000,
AppConfig: fakeApplicationConfigRetriever,
Metrics: chaincodeMetrics,
UseWriteBatch: true,
MaxSizeWriteBatch: 1000,
UseGetMultipleKeys: true,
MaxSizeGetMultipleKeys: 1000,
}
chaincode.SetHandlerChatStream(handler, fakeChatStream)
chaincode.SetHandlerChaincodeID(handler, "test-handler-name:1.0")
Expand Down Expand Up @@ -2786,8 +2788,10 @@ var _ = Describe("Handler", func() {
}))

chaincodeAdditionalParams := &pb.ChaincodeAdditionalParams{
UseWriteBatch: true,
MaxSizeWriteBatch: 1000,
UseWriteBatch: true,
MaxSizeWriteBatch: 1000,
UseGetMultipleKeys: true,
MaxSizeGetMultipleKeys: 1000,
}
payloadBytes, err := proto.Marshal(chaincodeAdditionalParams)
Expect(err).NotTo(HaveOccurred())
Expand Down
47 changes: 46 additions & 1 deletion integration/chaincode/multi/chaincode.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,19 @@ func (t *Operations) Invoke(stub shim.ChaincodeStubInterface) *pb.Response {
return shim.Error("Incorrect number of arguments. Expecting 1")
}
return t.putPrivateKey(stub, args[1])
case "get-multiple-keys":
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting 1")
}
return t.getMultiple(stub, args[0])
default:
// error
fmt.Println("invoke did not find func: " + function)
return shim.Error("Received unknown function invocation")
}
}

// both params should be marshalled json data and base64 encoded
// put to state keys from "key0" to "key(cntCall-1)"
func (t *Operations) put(stub shim.ChaincodeStubInterface, numberCallsPut string) *pb.Response {
cntCall, _ := strconv.Atoi(numberCallsPut)

Expand Down Expand Up @@ -97,3 +102,43 @@ func (t *Operations) putPrivateKey(stub shim.ChaincodeStubInterface, numberCalls
}
return shim.Success(nil)
}

// getMultiple - get multiple states
func (t *Operations) getMultiple(stub shim.ChaincodeStubInterface, countKeys string) *pb.Response {
num, _ := strconv.Atoi(countKeys)

keys := make([]string, 0, num)

keys = append(keys, "non-exist-key")
for i := range num {
key := "key" + strconv.Itoa(i)
keys = append(keys, key)
}

resps, err := stub.GetMultipleStates(keys...)
if err != nil {
return shim.Error(err.Error())
}

if len(resps) != num+1 {
return shim.Error("number of results is not correct")
}

// non exist key return nil
if resps[0] != nil {
errStr := fmt.Sprintf("incorrect result %d elem, got %v", 0, string(resps[0]))
return shim.Error(errStr)
}

if string(resps[1]) != "key"+strconv.Itoa(0) {
errStr := fmt.Sprintf("incorrect result %d elem, got %v", 0, string(resps[1]))
return shim.Error(errStr)
}

if string(resps[num]) != "key"+strconv.Itoa(num-1) {
errStr := fmt.Sprintf("incorrect result %d elem, got %v", 0, string(resps[num]))
return shim.Error(errStr)
}

return shim.Success(nil)
}
Loading

0 comments on commit 5d2b6ed

Please sign in to comment.