Skip to content

Commit

Permalink
[FAB-8607]: Ledger Client
Browse files Browse the repository at this point in the history
Change-Id: I3d3c69e3a18e0c03e2a018534394d696333d7c37
Signed-off-by: Sandra Vrtikapa <sandra.vrtikapa@securekey.com>
  • Loading branch information
sandrask committed Mar 1, 2018
1 parent 87f5d77 commit c3bbc2c
Show file tree
Hide file tree
Showing 9 changed files with 689 additions and 11 deletions.
406 changes: 406 additions & 0 deletions pkg/client/ledger/ledger.go

Large diffs are not rendered by default.

74 changes: 74 additions & 0 deletions pkg/client/ledger/opts.go
@@ -0,0 +1,74 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package ledger

import "github.com/hyperledger/fabric-sdk-go/pkg/context/api/fab"

const (
minTargets = 1
maxTargets = 1
)

// ClientOption describes a functional parameter for the New constructor
type ClientOption func(*Client) error

// WithDefaultTargetFilter option to configure new
func WithDefaultTargetFilter(filter TargetFilter) ClientOption {
return func(rmc *Client) error {
rmc.filter = filter
return nil
}
}

//RequestOption func for each Opts argument
type RequestOption func(opts *Opts) error

// TargetFilter allows for filtering target peers
type TargetFilter interface {
// Accept returns true if peer should be included in the list of target peers
Accept(peer fab.Peer) bool
}

//Opts contains options for operations performed by LedgerClient
type Opts struct {
Targets []fab.Peer // target peers
TargetFilter TargetFilter // target filter
MaxTargets int // maximum number of targets to select
MinTargets int // min number of targets that have to respond with no error (or agree on result)
}

//WithTargets encapsulates fab.Peer targets to ledger RequestOption
func WithTargets(targets ...fab.Peer) RequestOption {
return func(opts *Opts) error {
opts.Targets = targets
return nil
}
}

//WithTargetFilter encapsulates TargetFilter targets to ledger RequestOption
func WithTargetFilter(targetFilter TargetFilter) RequestOption {
return func(opts *Opts) error {
opts.TargetFilter = targetFilter
return nil
}
}

//WithMaxTargets encapsulates max targets to ledger RequestOption
func WithMaxTargets(maxTargets int) RequestOption {
return func(opts *Opts) error {
opts.MaxTargets = maxTargets
return nil
}
}

//WithMinTargets encapsulates min targets to ledger RequestOption
func WithMinTargets(minTargets int) RequestOption {
return func(opts *Opts) error {
opts.MinTargets = minTargets
return nil
}
}
9 changes: 8 additions & 1 deletion pkg/context/api/fab/channel.go
Expand Up @@ -59,7 +59,7 @@ type Channel interface {

// ChannelLedger provides access to the underlying ledger for a channel.
type ChannelLedger interface {
QueryInfo(targets []ProposalProcessor) ([]*common.BlockchainInfo, error)
QueryInfo(targets []ProposalProcessor) ([]*BlockchainInfoResponse, error)
QueryBlock(blockNumber int, targets []ProposalProcessor) ([]*common.Block, error)
QueryBlockByHash(blockHash []byte, targets []ProposalProcessor) ([]*common.Block, error)
QueryTransaction(transactionID TransactionID, targets []ProposalProcessor) ([]*pb.ProcessedTransaction, error)
Expand Down Expand Up @@ -104,3 +104,10 @@ type Versions struct {
WriteSet *common.ConfigGroup
Channel *common.ConfigGroup
}

// BlockchainInfoResponse wraps blockchain info with endorser info
type BlockchainInfoResponse struct {
BCI *common.BlockchainInfo
Endorser string
Status int32
}
2 changes: 1 addition & 1 deletion pkg/fab/channel/channel.go
Expand Up @@ -341,7 +341,7 @@ func (c *Channel) QueryInfo() (*common.BlockchainInfo, error) {
if err != nil {
return nil, err
}
return resps[0], err
return resps[0].BCI, err
}

// QueryBlockByHash queries the ledger for Block by block hash.
Expand Down
14 changes: 7 additions & 7 deletions pkg/fab/channel/ledger.go
Expand Up @@ -37,19 +37,19 @@ func NewLedger(ctx context.Context, chName string) (*Ledger, error) {

// QueryInfo queries for various useful information on the state of the channel
// (height, known peers).
func (c *Ledger) QueryInfo(targets []fab.ProposalProcessor) ([]*common.BlockchainInfo, error) {
func (c *Ledger) QueryInfo(targets []fab.ProposalProcessor) ([]*fab.BlockchainInfoResponse, error) {
logger.Debug("queryInfo - start")

cir := createChannelInfoInvokeRequest(c.chName)
tprs, errs := queryChaincode(c.ctx, fab.SystemChannel, cir, targets)
tprs, errs := queryChaincode(c.ctx, c.chName, cir, targets)

responses := []*common.BlockchainInfo{}
responses := []*fab.BlockchainInfoResponse{}
for _, tpr := range tprs {
r, err := createBlockchainInfo(tpr)
if err != nil {
errs = multi.Append(errs, errors.WithMessage(err, "From target: "+tpr.Endorser))
} else {
responses = append(responses, r)
responses = append(responses, &fab.BlockchainInfoResponse{Endorser: tpr.Endorser, Status: tpr.Status, BCI: r})
}
}
return responses, errs
Expand All @@ -74,7 +74,7 @@ func (c *Ledger) QueryBlockByHash(blockHash []byte, targets []fab.ProposalProces
}

cir := createBlockByHashInvokeRequest(c.chName, blockHash)
tprs, errs := queryChaincode(c.ctx, fab.SystemChannel, cir, targets)
tprs, errs := queryChaincode(c.ctx, c.chName, cir, targets)

responses := []*common.Block{}
for _, tpr := range tprs {
Expand All @@ -99,7 +99,7 @@ func (c *Ledger) QueryBlock(blockNumber int, targets []fab.ProposalProcessor) ([
}

cir := createBlockByNumberInvokeRequest(c.chName, blockNumber)
tprs, errs := queryChaincode(c.ctx, fab.SystemChannel, cir, targets)
tprs, errs := queryChaincode(c.ctx, c.chName, cir, targets)

responses := []*common.Block{}
for _, tpr := range tprs {
Expand Down Expand Up @@ -128,7 +128,7 @@ func createCommonBlock(tpr *fab.TransactionProposalResponse) (*common.Block, err
func (c *Ledger) QueryTransaction(transactionID fab.TransactionID, targets []fab.ProposalProcessor) ([]*pb.ProcessedTransaction, error) {

cir := createTransactionByIDInvokeRequest(c.chName, transactionID)
tprs, errs := queryChaincode(c.ctx, fab.SystemChannel, cir, targets)
tprs, errs := queryChaincode(c.ctx, c.chName, cir, targets)

responses := []*pb.ProcessedTransaction{}
for _, tpr := range tprs {
Expand Down
29 changes: 29 additions & 0 deletions pkg/fabsdk/client.go
Expand Up @@ -8,6 +8,7 @@ package fabsdk

import (
"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
"github.com/hyperledger/fabric-sdk-go/pkg/client/ledger"
"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
"github.com/hyperledger/fabric-sdk-go/pkg/context"
"github.com/hyperledger/fabric-sdk-go/pkg/context/api/core"
Expand Down Expand Up @@ -178,6 +179,34 @@ func (c *ClientContext) ResourceMgmt(opts ...ClientOption) (*resmgmt.Client, err

}

// Ledger returns a client API for querying ledger
func (c *ClientContext) Ledger(id string, opts ...ClientOption) (*ledger.Client, error) {
p, err := c.provider()
if err != nil {
return &ledger.Client{}, errors.WithMessage(err, "unable to get client provider context")
}
o, err := newClientOptions(opts)
if err != nil {
return &ledger.Client{}, errors.WithMessage(err, "unable to retrieve client options")
}
session := newSession(p.identity, p.providers.ChannelProvider())

discovery := p.providers.DiscoveryProvider()
discService, err := discovery.NewDiscoveryService(id)
if err != nil {
return nil, err
}

ctx := ledger.Context{
ProviderContext: p.providers,
IdentityContext: session,
DiscoveryService: discService,
}

return ledger.New(ctx, id, ledger.WithDefaultTargetFilter(o.targetFilter))

}

// Channel returns a client API for transacting on a channel.
func (c *ClientContext) Channel(id string, opts ...ClientOption) (*channel.Client, error) {
p, err := c.provider()
Expand Down
4 changes: 2 additions & 2 deletions test/integration/fab/channel_ledger_test.go
Expand Up @@ -98,7 +98,7 @@ func TestLedgerQueries(t *testing.T) {
}

// Test Query Info -- verify block size changed after transaction
if (bciAfterTx[0].Height - bciBeforeTx[0].Height) <= 0 {
if (bciAfterTx[0].BCI.Height - bciBeforeTx[0].BCI.Height) <= 0 {
t.Fatalf("Block size did not increase after transaction")
}

Expand Down Expand Up @@ -178,7 +178,7 @@ func testQueryBlock(t *testing.T, ledger fab.ChannelLedger, targets []fab.Propos

for i, bci := range bcis {
// Test Query Block by Hash - retrieve current block by hash
block, err := ledger.QueryBlockByHash(bci.CurrentBlockHash, targets[i:i+1])
block, err := ledger.QueryBlockByHash(bci.BCI.CurrentBlockHash, targets[i:i+1])
if err != nil {
t.Fatalf("QueryBlockByHash return error: %v", err)
}
Expand Down
62 changes: 62 additions & 0 deletions test/integration/orgs/multiple_orgs_test.go
Expand Up @@ -10,6 +10,7 @@ import (
"math"
"path"
"strconv"
"strings"
"testing"
"time"

Expand All @@ -20,6 +21,7 @@ import (
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
"github.com/pkg/errors"

"github.com/hyperledger/fabric-sdk-go/pkg/client/ledger"
"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
"github.com/hyperledger/fabric-sdk-go/pkg/context/api/core"
"github.com/hyperledger/fabric-sdk-go/pkg/context/api/fab"
Expand Down Expand Up @@ -144,6 +146,38 @@ func TestOrgsEndToEnd(t *testing.T) {
}
initial, _ := strconv.Atoi(string(response.Payload))

// Ledger client will verify blockchain info
ledgerClient, err := sdk.NewClient(fabsdk.WithUser("Admin")).Ledger("orgchannel")
if err != nil {
t.Fatalf("Failed to create new ledger client: %s", err)
}

channelCfg, err := ledgerClient.QueryConfig(ledger.WithTargets(orgTestPeer0.(fab.Peer), orgTestPeer1.(fab.Peer)), ledger.WithMinTargets(2))
if err != nil {
t.Fatalf("QueryConfig return error: %v", err)
}
if len(channelCfg.Orderers()) == 0 {
t.Fatalf("Failed to retrieve channel orderers")
}
expectedOrderer := "orderer.example.com"
if !strings.Contains(channelCfg.Orderers()[0], expectedOrderer) {
t.Fatalf("Expecting %s, got %s", expectedOrderer, channelCfg.Orderers()[0])
}

ledgerInfoBefore, err := ledgerClient.QueryInfo(ledger.WithTargets(orgTestPeer0.(fab.Peer), orgTestPeer1.(fab.Peer)), ledger.WithMinTargets(2), ledger.WithMaxTargets(3))
if err != nil {
t.Fatalf("QueryInfo return error: %v", err)
}

// Test Query Block by Hash - retrieve current block by hash
block, err := ledgerClient.QueryBlockByHash(ledgerInfoBefore.BCI.CurrentBlockHash, ledger.WithTargets(orgTestPeer0.(fab.Peer), orgTestPeer1.(fab.Peer)), ledger.WithMinTargets(2))
if err != nil {
t.Fatalf("QueryBlockByHash return error: %v", err)
}
if block == nil {
t.Fatalf("Block info not available")
}

// Org2 user moves funds on org2 peer
response, err = chClientOrg2User.Execute(channel.Request{ChaincodeID: "exampleCC", Fcn: "invoke", Args: integration.ExampleCCTxArgs()}, channel.WithProposalProcessor(orgTestPeer1))
if err != nil {
Expand All @@ -153,6 +187,34 @@ func TestOrgsEndToEnd(t *testing.T) {
// Assert that funds have changed value on org1 peer
verifyValue(t, chClientOrg1User, initial+1)

// Get latest block chain info
ledgerInfoAfter, err := ledgerClient.QueryInfo(ledger.WithTargets(orgTestPeer0.(fab.Peer), orgTestPeer1.(fab.Peer)), ledger.WithMinTargets(2))
if err != nil {
t.Fatalf("QueryInfo return error: %v", err)
}

if ledgerInfoAfter.BCI.Height-ledgerInfoBefore.BCI.Height <= 0 {
t.Fatalf("Block size did not increase after transaction")
}

// Test Query Block by Hash - retrieve current block by number
block, err = ledgerClient.QueryBlock(int(ledgerInfoAfter.BCI.Height)-1, ledger.WithTargets(orgTestPeer0.(fab.Peer), orgTestPeer1.(fab.Peer)), ledger.WithMinTargets(2))
if err != nil {
t.Fatalf("QueryBlock return error: %v", err)
}
if block == nil {
t.Fatalf("Block info not available")
}

// Get transaction info
transactionInfo, err := ledgerClient.QueryTransaction(response.TransactionID, ledger.WithTargets(orgTestPeer0.(fab.Peer), orgTestPeer1.(fab.Peer)), ledger.WithMinTargets(2))
if err != nil {
t.Fatalf("QueryTransaction return error: %v", err)
}
if transactionInfo.TransactionEnvelope == nil {
t.Fatalf("Transaction info missing")
}

// Start chaincode upgrade process (install and instantiate new version of exampleCC)
installCCReq = resmgmt.InstallCCRequest{Name: "exampleCC", Path: "github.com/example_cc", Version: "1", Package: ccPkg}

Expand Down

0 comments on commit c3bbc2c

Please sign in to comment.