Skip to content

Commit

Permalink
[FAB-2911]Fix JSON-unmarshal for Generic Interface
Browse files Browse the repository at this point in the history
GOLANG JSON package stores integer in float format.
In fabric Couch DB state ledger code performs a json unmarshalling
on the data wrapper using generic interface data-type.
For JSON document stored in the state ledger that has integer
with digit size >=7; now gets unmarshalled with a E (float) notation which
is eventually passed to the user chain-code upon for eg. shim GetState API.

Unmarshalling at the user-chaincode level result in an error indicating
float cannot be converted to integer type.

To fix this issue, it is neccessary to use NewDecoder API of JSON package
and retains the number representation in such case.

Change-Id: I650cf88dacff84e5e9a185bfa44984a2814488a4
Signed-off-by: rhegde <rhegde@cls-bank.com>
  • Loading branch information
rhegde authored and Chris Elder committed Mar 30, 2017
1 parent 561443e commit 1779125
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 15 deletions.
47 changes: 35 additions & 12 deletions core/ledger/kvledger/txmgmt/statedb/commontests/test_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,20 +208,22 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) {
batch.Put("ns1", "key9", []byte(jsonValue9), version.NewHeight(1, 9))
jsonValue10 := "{\"asset_name\": \"marble10\",\"color\": \"green\",\"size\": 10,\"owner\": \"mary\"}"
batch.Put("ns1", "key10", []byte(jsonValue10), version.NewHeight(1, 10))
jsonValue11 := "{\"asset_name\": \"marble11\",\"color\": \"cyan\",\"size\": 1000007,\"owner\": \"joe\"}"
batch.Put("ns1", "key11", []byte(jsonValue11), version.NewHeight(1, 11))

//add keys for a separate namespace
batch.Put("ns2", "key1", []byte(jsonValue1), version.NewHeight(1, 11))
batch.Put("ns2", "key2", []byte(jsonValue2), version.NewHeight(1, 12))
batch.Put("ns2", "key3", []byte(jsonValue3), version.NewHeight(1, 13))
batch.Put("ns2", "key4", []byte(jsonValue4), version.NewHeight(1, 14))
batch.Put("ns2", "key5", []byte(jsonValue5), version.NewHeight(1, 15))
batch.Put("ns2", "key6", []byte(jsonValue6), version.NewHeight(1, 16))
batch.Put("ns2", "key7", []byte(jsonValue7), version.NewHeight(1, 17))
batch.Put("ns2", "key8", []byte(jsonValue8), version.NewHeight(1, 18))
batch.Put("ns2", "key9", []byte(jsonValue9), version.NewHeight(1, 19))
batch.Put("ns2", "key10", []byte(jsonValue10), version.NewHeight(1, 20))

savePoint := version.NewHeight(2, 21)
batch.Put("ns2", "key1", []byte(jsonValue1), version.NewHeight(1, 12))
batch.Put("ns2", "key2", []byte(jsonValue2), version.NewHeight(1, 13))
batch.Put("ns2", "key3", []byte(jsonValue3), version.NewHeight(1, 14))
batch.Put("ns2", "key4", []byte(jsonValue4), version.NewHeight(1, 15))
batch.Put("ns2", "key5", []byte(jsonValue5), version.NewHeight(1, 16))
batch.Put("ns2", "key6", []byte(jsonValue6), version.NewHeight(1, 17))
batch.Put("ns2", "key7", []byte(jsonValue7), version.NewHeight(1, 18))
batch.Put("ns2", "key8", []byte(jsonValue8), version.NewHeight(1, 19))
batch.Put("ns2", "key9", []byte(jsonValue9), version.NewHeight(1, 20))
batch.Put("ns2", "key10", []byte(jsonValue10), version.NewHeight(1, 21))

savePoint := version.NewHeight(2, 22)
db.ApplyUpdates(batch, savePoint)

// query for owner=jerry, use namespace "ns1"
Expand Down Expand Up @@ -435,4 +437,25 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) {
testutil.AssertNoError(t, err, "")
testutil.AssertNil(t, queryResult1)

// query with integer with digit-count equals 7 and response received is also received
// with same digit-count and there is no float transformation
itr, err = db.ExecuteQuery("ns1", "{\"selector\":{\"$and\":[{\"size\":{\"$eq\": 1000007}}]}}")
testutil.AssertNoError(t, err, "")

// verify one jerry result
queryResult1, err = itr.Next()
testutil.AssertNoError(t, err, "")
testutil.AssertNotNil(t, queryResult1)
versionedQueryRecord = queryResult1.(*statedb.VersionedQueryRecord)
stringRecord = string(versionedQueryRecord.Record)
bFoundRecord = strings.Contains(stringRecord, "joe")
testutil.AssertEquals(t, bFoundRecord, true)
bFoundRecord = strings.Contains(stringRecord, "1000007")
testutil.AssertEquals(t, bFoundRecord, true)

// verify no more results
queryResult2, err = itr.Next()
testutil.AssertNoError(t, err, "")
testutil.AssertNil(t, queryResult2)

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package statecouchdb

import (
"bytes"
"encoding/json"
"fmt"
"reflect"
Expand Down Expand Up @@ -66,7 +67,9 @@ func ApplyQueryWrapper(namespace, queryString string, queryLimit, querySkip int)
jsonQueryMap := make(map[string]interface{})

//unmarshal the selected json into the generic map
err := json.Unmarshal([]byte(queryString), &jsonQueryMap)
decoder := json.NewDecoder(bytes.NewBuffer([]byte(queryString)))
decoder.UseNumber()
err := decoder.Decode(&jsonQueryMap)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -167,6 +170,10 @@ func processAndWrapQuery(jsonQueryMap map[string]interface{}) {
//intercept the float64 case and prevent the []interface{} case from
//incorrectly processing the float64

case json.Number:
//intercept the Number case and prevent the []interface{} case from
//incorrectly processing the float64

//if the type is an array, then iterate through the items
case []interface{}:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,3 +366,18 @@ func TestQueryWithUseDesignDocAndIndexName(t *testing.T) {
testutil.AssertEquals(t, strings.Count(wrappedQuery, "\"use_index\":[\"_design/testDoc\",\"testIndexName\"]"), 1)

}

//TestQueryWithLargeInteger tests query with large integer
func TestQueryWithLargeInteger(t *testing.T) {

rawQuery := []byte(`{"selector":{"$and":[{"size":{"$eq": 1000007}}]}}`)

wrappedQuery, err := ApplyQueryWrapper("ns1", string(rawQuery), 10000, 0)

//Make sure the query did not throw an exception
testutil.AssertNoError(t, err, "Unexpected error thrown when for query JSON")

//check to make sure the default selector is added
testutil.AssertEquals(t, strings.Count(wrappedQuery, "{\"$eq\":1000007}"), 1)

}
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ func removeDataWrapper(wrappedValue []byte, attachments []couchdb.Attachment) ([
jsonResult := make(map[string]interface{})

//unmarshal the selected json into the generic map
json.Unmarshal(wrappedValue, &jsonResult)
decoder := json.NewDecoder(bytes.NewBuffer(wrappedValue))
decoder.UseNumber()
_ = decoder.Decode(&jsonResult)

// handle binary or json data
if jsonResult[dataWrapper] == nil && attachments != nil { // binary attachment
Expand Down
5 changes: 4 additions & 1 deletion core/ledger/util/couchdb/couchdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,8 +504,11 @@ func createAttachmentPart(couchDoc *CouchDoc, defaultBoundary string) (bytes.Buf

//create a generic map
genericMap := make(map[string]interface{})

//unmarshal the data into the generic map
json.Unmarshal(couchDoc.JSONValue, &genericMap)
decoder := json.NewDecoder(bytes.NewBuffer(couchDoc.JSONValue))
decoder.UseNumber()
decoder.Decode(&genericMap)

//add all key/values to the attachmentJSONMap
for jsonKey, jsonValue := range genericMap {
Expand Down

0 comments on commit 1779125

Please sign in to comment.