Skip to content

Commit

Permalink
[FAB-1984] Remove GetQueryResult from QSCC
Browse files Browse the repository at this point in the history
In order to control access, all queries will need to go through
application chaincode. This change is in response to early
v1 feedback.

See marbles02 for an example of calling GetQueryResult from application chaincode.

In the future if there is capability to place ACL on peer APIs,
then it would be possible to expose GetQueryResult as a peer
API (or via QSCC).

Change-Id: I1613563caf823e45fdfebeaa133f4ec523716cec
Signed-off-by: denyeart <enyeart@us.ibm.com>
  • Loading branch information
denyeart committed Feb 11, 2017
1 parent 9b1bcf5 commit b254b9b
Show file tree
Hide file tree
Showing 2 changed files with 0 additions and 110 deletions.
95 changes: 0 additions & 95 deletions core/scc/qscc/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,11 @@ limitations under the License.
package qscc

import (
"bytes"
"fmt"
"strconv"

"github.com/op/go-logging"
"github.com/spf13/viper"

commonledger "github.com/hyperledger/fabric/common/ledger"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/peer"
Expand All @@ -37,7 +34,6 @@ import (
// - GetBlockByNumber returns a block
// - GetBlockByHash returns a block
// - GetTransactionByID returns a transaction
// - GetQueryResult returns result of a freeform query
type LedgerQuerier struct {
}

Expand All @@ -49,7 +45,6 @@ const (
GetBlockByNumber string = "GetBlockByNumber"
GetBlockByHash string = "GetBlockByHash"
GetTransactionByID string = "GetTransactionByID"
GetQueryResult string = "GetQueryResult"
)

// Init is called once per chain when the chain is created.
Expand All @@ -68,11 +63,6 @@ func (e *LedgerQuerier) Init(stub shim.ChaincodeStubInterface) pb.Response {
// # GetBlockByNumber: Return the block specified by block number in args[2]
// # GetBlockByHash: Return the block specified by block hash in args[2]
// # GetTransactionByID: Return the transaction specified by ID in args[2]
// # GetQueryResult: Return the result of executing the specified native
// query string in args[2]. Note that this only works if plugged in database
// supports it. The result is a JSON array in a byte array. Note that error
// may be returned together with a valid partial result as error might occur
// during accummulating records from the ledger
func (e *LedgerQuerier) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
args := stub.GetArgs()

Expand All @@ -97,8 +87,6 @@ func (e *LedgerQuerier) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
// TODO: Handle ACL

switch fname {
case GetQueryResult:
return getQueryResult(targetLedger, args[2])
case GetTransactionByID:
return getTransactionByID(targetLedger, args[2])
case GetBlockByNumber:
Expand All @@ -112,89 +100,6 @@ func (e *LedgerQuerier) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
return shim.Error(fmt.Sprintf("Requested function %s not found.", fname))
}

// Execute the specified query string
func getQueryResult(vledger ledger.PeerLedger, query []byte) (res pb.Response) {
if query == nil {
return shim.Error("Query string must not be nil.")
}
qstring := string(query)
var qexe ledger.QueryExecutor
var ri commonledger.ResultsIterator
var err error

// We install a recover() to gain control in 2 cases
// 1) bytes.Buffer panics, which happens when out of memory
// This is a safety measure beyond the config limit variable
// 2) plugin db driver might panic
// We recover by stopping the query and return the panic error.
defer func() {
if panicValue := recover(); panicValue != nil {
if qscclogger.IsEnabledFor(logging.DEBUG) {
qscclogger.Debugf("Recovering panic: %s", panicValue)
}
res = shim.Error(fmt.Sprintf("Error recovery: %s", panicValue))
}
}()

if qexe, err = vledger.NewQueryExecutor(); err != nil {
return shim.Error(err.Error())
}
if ri, err = qexe.ExecuteQuery(qstring); err != nil {
return shim.Error(err.Error())
}
defer ri.Close()

limit := viper.GetInt("ledger.state.couchDBConfig.queryLimit")

// buffer is a JSON array containing QueryRecords
var buffer bytes.Buffer
buffer.WriteString("[")

var qresult commonledger.QueryResult
bArrayMemberAlreadyWritten := false
qresult, err = ri.Next()
for r := 0; qresult != nil && err == nil && r < limit; r++ {
if qr, ok := qresult.(*ledger.QueryRecord); ok {
// Add a comma before array members, suppress it for the first array member
if bArrayMemberAlreadyWritten == true {
buffer.WriteString(",")
}
collectRecord(&buffer, qr)
bArrayMemberAlreadyWritten = true
}
qresult, err = ri.Next()
}

buffer.WriteString("]")

// Return what we have accummulated
ret := buffer.Bytes()
return shim.Success(ret)
}

// Append QueryRecord into buffer as a JSON record of the form {namespace, key, record}
// type QueryRecord struct {
// Namespace string
// Key string
// Record []byte
// }
func collectRecord(buffer *bytes.Buffer, rec *ledger.QueryRecord) {
buffer.WriteString("{\"Namespace\":")
buffer.WriteString("\"")
buffer.WriteString(rec.Namespace)
buffer.WriteString("\"")

buffer.WriteString(", \"Key\":")
buffer.WriteString("\"")
buffer.WriteString(rec.Key)
buffer.WriteString("\"")

buffer.WriteString(", \"Record\":")
// Record is a JSON object, so we write as-is
buffer.WriteString(string(rec.Record))
buffer.WriteString("}")
}

func getTransactionByID(vledger ledger.PeerLedger, tid []byte) pb.Response {
if tid == nil {
return shim.Error("Transaction ID must not be nil.")
Expand Down
15 changes: 0 additions & 15 deletions core/scc/qscc/querier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,3 @@ func TestQueryGetBlockByHash(t *testing.T) {
t.Fatalf("qscc GetBlockByHash should have failed with invalid hash: 0")
}
}

func TestQueryGetQueryResult(t *testing.T) {
viper.Set("peer.fileSystemPath", "/var/hyperledger/test7/")
defer os.RemoveAll("/var/hyperledger/test7/")
peer.MockInitialize()
peer.MockCreateChain("mytestchainid7")

e := new(LedgerQuerier)
stub := shim.NewMockStub("LedgerQuerier", e)
qstring := "{\"selector\":{\"key\":\"value\"}}"
args := [][]byte{[]byte(GetQueryResult), []byte("mytestchainid7"), []byte(qstring)}
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
t.Fatalf("qscc GetQueryResult should have failed with invalid query: abc")
}
}

0 comments on commit b254b9b

Please sign in to comment.