Skip to content

Commit

Permalink
[FAB-8191] Split ledger queries from channel
Browse files Browse the repository at this point in the history
This patch creates a new ChannelLedger type that
contains query methods to a channel's underlying ledger.

Change-Id: Ie94e04b6139075af6462d239c47689d18e1bca59
Signed-off-by: Troy Ronda <[email protected]>
  • Loading branch information
troyronda committed Feb 9, 2018
1 parent f2b1c3b commit bc269e6
Show file tree
Hide file tree
Showing 9 changed files with 601 additions and 452 deletions.
9 changes: 9 additions & 0 deletions api/apifabclient/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ type Channel interface {
QueryConfigBlock(peers []Peer, minResponses int) (*common.ConfigEnvelope, error)
}

// ChannelLedger provides access to the underlying ledger for a channel.
type ChannelLedger interface {
QueryInfo(targets []ProposalProcessor) (*common.BlockchainInfo, error)
QueryBlock(blockNumber int, targets []ProposalProcessor) (*common.Block, error)
QueryBlockByHash(blockHash []byte, targets []ProposalProcessor) (*common.Block, error)
QueryTransaction(transactionID string, targets []ProposalProcessor) (*pb.ProcessedTransaction, error)
QueryInstantiatedChaincodes(targets []ProposalProcessor) (*pb.ChaincodeQueryResponse, error)
}

// OrgAnchorPeer contains information about an anchor peer on this channel
type OrgAnchorPeer struct {
Org string
Expand Down
106 changes: 100 additions & 6 deletions pkg/fabric-client/channel/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,20 @@ SPDX-License-Identifier: Apache-2.0
package channel

import (
fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient"

"crypto/x509"

"encoding/pem"

"regexp"
"strings"

"regexp"
"github.com/pkg/errors"

"github.com/hyperledger/fabric-sdk-go/api/apiconfig"
fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient"
"github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/orderer"
"github.com/hyperledger/fabric-sdk-go/pkg/logging"
"github.com/pkg/errors"
"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer"
)

var logger = logging.NewLogger("fabric_sdk_go")
Expand Down Expand Up @@ -327,3 +326,98 @@ func resolveOrdererURL(ordererURL string) string {
}
return "grpcs://" + ordererURL
}

// QueryInfo queries for various useful information on the state of the channel
// (height, known peers).
// This query will be made to the primary peer.
func (c *Channel) QueryInfo() (*common.BlockchainInfo, error) {
l := NewLedger(c.clientContext, c.name)
resps, err := l.QueryInfo([]fab.ProposalProcessor{c.PrimaryPeer()})
if err != nil {
return nil, err
}
return resps[0], err
}

// QueryBlockByHash queries the ledger for Block by block hash.
// This query will be made to the primary peer.
// Returns the block.
func (c *Channel) QueryBlockByHash(blockHash []byte) (*common.Block, error) {
l := NewLedger(c.clientContext, c.name)
resps, err := l.QueryBlockByHash(blockHash, []fab.ProposalProcessor{c.PrimaryPeer()})
if err != nil {
return nil, err
}
return resps[0], err
}

// QueryBlock queries the ledger for Block by block number.
// This query will be made to the primary peer.
// blockNumber: The number which is the ID of the Block.
// It returns the block.
func (c *Channel) QueryBlock(blockNumber int) (*common.Block, error) {
l := NewLedger(c.clientContext, c.name)
resps, err := l.QueryBlock(blockNumber, []fab.ProposalProcessor{c.PrimaryPeer()})
if err != nil {
return nil, err
}
return resps[0], err
}

// QueryTransaction queries the ledger for Transaction by number.
// This query will be made to the primary peer.
// Returns the ProcessedTransaction information containing the transaction.
// TODO: add optional target
func (c *Channel) QueryTransaction(transactionID string) (*pb.ProcessedTransaction, error) {
l := NewLedger(c.clientContext, c.name)
resps, err := l.QueryTransaction(transactionID, []fab.ProposalProcessor{c.PrimaryPeer()})
if err != nil {
return nil, err
}
return resps[0], err
}

// QueryInstantiatedChaincodes queries the instantiated chaincodes on this channel.
// This query will be made to the primary peer.
func (c *Channel) QueryInstantiatedChaincodes() (*pb.ChaincodeQueryResponse, error) {
l := NewLedger(c.clientContext, c.name)
resps, err := l.QueryInstantiatedChaincodes([]fab.ProposalProcessor{c.PrimaryPeer()})
if err != nil {
return nil, err
}
return resps[0], err

}

// QueryConfigBlock returns the current configuration block for the specified channel. If the
// peer doesn't belong to the channel, return error
func (c *Channel) QueryConfigBlock(peers []fab.Peer, minResponses int) (*common.ConfigEnvelope, error) {
l := NewLedger(c.clientContext, c.name)
return l.QueryConfigBlock(peers, minResponses)
}

// QueryByChaincode sends a proposal to one or more endorsing peers that will be handled by the chaincode.
// This request will be presented to the chaincode 'invoke' and must understand
// from the arguments that this is a query request. The chaincode must also return
// results in the byte array format and the caller will have to be able to decode.
// these results.
func (c *Channel) QueryByChaincode(request fab.ChaincodeInvokeRequest) ([][]byte, error) {
targets, err := c.chaincodeInvokeRequestAddDefaultPeers(request.Targets)
if err != nil {
return nil, err
}
resps, err := queryChaincode(c.clientContext, c.name, request, targets)
return filterProposalResponses(resps, err)
}

// QueryBySystemChaincode invokes a chaincode that isn't part of a channel.
//
// TODO: This function's name is confusing - call the normal QueryByChaincode for system chaincode on a channel.
func (c *Channel) QueryBySystemChaincode(request fab.ChaincodeInvokeRequest) ([][]byte, error) {
targets, err := c.chaincodeInvokeRequestAddDefaultPeers(request.Targets)
if err != nil {
return nil, err
}
resps, err := queryChaincode(c.clientContext, systemChannel, request, targets)
return filterProposalResponses(resps, err)
}
40 changes: 40 additions & 0 deletions pkg/fabric-client/channel/channel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package channel

import (
"fmt"
"reflect"
"testing"

fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient"
Expand Down Expand Up @@ -165,6 +166,45 @@ func TestPrimaryPeer(t *testing.T) {

}

func TestQueryOnSystemChannel(t *testing.T) {
channel, _ := setupChannel(systemChannel)
peer := mocks.MockPeer{MockName: "Peer1", MockURL: "http://peer1.com", MockRoles: []string{}, MockCert: nil, Status: 200}
err := channel.AddPeer(&peer)
if err != nil {
t.Fatalf("Error adding peer to channel: %s", err)
}

request := fab.ChaincodeInvokeRequest{
ChaincodeID: "ccID",
Fcn: "method",
Args: [][]byte{[]byte("arg")},
}
if _, err := channel.QueryByChaincode(request); err != nil {
t.Fatalf("Error invoking chaincode on system channel: %s", err)
}
}

func TestQueryBySystemChaincode(t *testing.T) {
channel, _ := setupTestChannel()

peer := mocks.MockPeer{MockName: "Peer1", MockURL: "http://peer1.com", MockRoles: []string{}, MockCert: nil, Payload: []byte("A"), Status: 200}
channel.AddPeer(&peer)

request := fab.ChaincodeInvokeRequest{
ChaincodeID: "cc",
Fcn: "Hello",
}
resp, err := channel.QueryBySystemChaincode(request)
if err != nil {
t.Fatalf("Failed to query: %s", err)
}
expectedResp := []byte("A")

if !reflect.DeepEqual(resp[0], expectedResp) {
t.Fatalf("Unexpected transaction proposal response: %v", resp)
}
}

func isValueInList(value string, list []string) bool {
for _, v := range list {
if v == value {
Expand Down
Loading

0 comments on commit bc269e6

Please sign in to comment.