github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/examples/ccchecker/chaincodes/chaincodes.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package chaincodes
    18  
    19  import (
    20  	"fmt"
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/hyperledger/fabric/msp"
    25  	"github.com/hyperledger/fabric/peer/chaincode"
    26  	"github.com/hyperledger/fabric/peer/common"
    27  	pb "github.com/hyperledger/fabric/protos/peer"
    28  
    29  	"golang.org/x/net/context"
    30  )
    31  
    32  //ShadowCCIntf interfaces to be implemented by shadow chaincodes
    33  type ShadowCCIntf interface {
    34  	//InitShadowCC initializes the shadow chaincode (will be called once for each chaincode)
    35  	InitShadowCC(initArgs []string)
    36  
    37  	//GetInvokeArgs gets invoke arguments from shadow
    38  	GetInvokeArgs(ccnum int, iter int) [][]byte
    39  
    40  	//PostInvoke passes the retvalue from the invoke to the shadow for post-processing
    41  	PostInvoke(args [][]byte, retval []byte) error
    42  
    43  	//GetQueryArgs mimics the Invoke and gets the query for an invoke
    44  	GetQueryArgs(ccnum int, iter int) [][]byte
    45  
    46  	//Validate the results against the query arguments
    47  	Validate(args [][]byte, value []byte) error
    48  
    49  	//GetNumQueries returns number of queries to perform
    50  	GetNumQueries(numSuccessfulInvokes int) int
    51  
    52  	//OverrideNumInvokes overrides the users number of invoke request
    53  	OverrideNumInvokes(numInvokesPlanned int) int
    54  }
    55  
    56  //CCClient chaincode properties, config and runtime
    57  type CCClient struct {
    58  	//-------------config properties ------------
    59  	//Name of the chaincode
    60  	Name string
    61  
    62  	//InitArgs used for deploying the chaincode
    63  	InitArgs []string
    64  
    65  	//Path to the chaincode
    66  	Path string
    67  
    68  	//NumFinalQueryAttempts number of times to try final query before giving up
    69  	NumFinalQueryAttempts int
    70  
    71  	//NumberOfInvokes number of iterations to do invoke on
    72  	NumberOfInvokes int
    73  
    74  	//DelayBetweenInvokeMs delay between each invoke
    75  	DelayBetweenInvokeMs int
    76  
    77  	//DelayBetweenQueryMs delay between each query
    78  	DelayBetweenQueryMs int
    79  
    80  	//TimeoutToAbortSecs timeout for aborting this chaincode processing
    81  	TimeoutToAbortSecs int
    82  
    83  	//Lang of chaincode
    84  	Lang string
    85  
    86  	//WaitAfterInvokeMs wait time before validating invokes for this chaincode
    87  	WaitAfterInvokeMs int
    88  
    89  	//Concurrency number of goroutines to spin
    90  	Concurrency int
    91  
    92  	//-------------runtime properties ------------
    93  	//Unique number assigned to this CC by CCChecker
    94  	ID int
    95  
    96  	//shadow CC where the chaincode stats is maintained
    97  	shadowCC ShadowCCIntf
    98  
    99  	//number of iterations a shadow cc actually wants
   100  	//this could be different from NumberOfInvokes
   101  	overriddenNumInvokes int
   102  
   103  	//numer of queries to perform
   104  	//retrieved from the shadow CC
   105  	numQueries int
   106  
   107  	//current iteration of invoke
   108  	currentInvokeIter int
   109  
   110  	//start of invokes in epoch seconds
   111  	invokeStartTime int64
   112  
   113  	//end of invokes in epoch seconds
   114  	invokeEndTime int64
   115  
   116  	//error that stopped invoke iterations
   117  	invokeErr error
   118  
   119  	//current iteration of query
   120  	currQueryIter []int
   121  
   122  	//did the query work ?
   123  	queryWorked []bool
   124  
   125  	//error on a query in an iteration
   126  	queryErrs []error
   127  }
   128  
   129  func (cc *CCClient) getChaincodeSpec(args [][]byte) *pb.ChaincodeSpec {
   130  	return &pb.ChaincodeSpec{
   131  		Type:        pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[cc.Lang]),
   132  		ChaincodeId: &pb.ChaincodeID{Path: cc.Path, Name: cc.Name},
   133  		Input:       &pb.ChaincodeInput{Args: args},
   134  	}
   135  }
   136  
   137  //doInvokes calls invoke for each iteration for the chaincode
   138  //Stops at the first invoke with error
   139  //currentInvokeIter contains the number of successful iterations
   140  func (cc *CCClient) doInvokes(ctxt context.Context, chainID string,
   141  	bc common.BroadcastClient, ec pb.EndorserClient, signer msp.SigningIdentity,
   142  	wg *sync.WaitGroup, quit func() bool) error {
   143  
   144  	//perhaps the shadow CC wants to override the number of iterations
   145  	cc.overriddenNumInvokes = cc.shadowCC.OverrideNumInvokes(cc.NumberOfInvokes)
   146  
   147  	var err error
   148  	for cc.currentInvokeIter = 0; cc.currentInvokeIter < cc.overriddenNumInvokes; cc.currentInvokeIter++ {
   149  		if quit() {
   150  			break
   151  		}
   152  		args := cc.shadowCC.GetInvokeArgs(cc.ID, cc.currentInvokeIter)
   153  
   154  		spec := cc.getChaincodeSpec(args)
   155  
   156  		if quit() {
   157  			break
   158  		}
   159  
   160  		var pResp *pb.ProposalResponse
   161  		if pResp, err = chaincode.ChaincodeInvokeOrQuery(spec, chainID, true, signer, ec, bc); err != nil {
   162  			cc.invokeErr = err
   163  			break
   164  		}
   165  
   166  		resp := pResp.Response.Payload
   167  		if err = cc.shadowCC.PostInvoke(args, resp); err != nil {
   168  			cc.invokeErr = err
   169  			break
   170  		}
   171  
   172  		if quit() {
   173  			break
   174  		}
   175  
   176  		//don't sleep for the last iter
   177  		if cc.DelayBetweenInvokeMs > 0 && cc.currentInvokeIter < (cc.overriddenNumInvokes-1) {
   178  			time.Sleep(time.Duration(cc.DelayBetweenInvokeMs) * time.Millisecond)
   179  		}
   180  	}
   181  
   182  	return err
   183  }
   184  
   185  //Run test over given number of iterations
   186  //  i will be unique across chaincodes and can be used as a key
   187  //    this is useful if chaincode occurs multiple times in the array of chaincodes
   188  func (cc *CCClient) Run(ctxt context.Context, chainID string, bc common.BroadcastClient, ec pb.EndorserClient, signer msp.SigningIdentity, wg *sync.WaitGroup) error {
   189  	defer wg.Done()
   190  
   191  	var (
   192  		quit bool
   193  		err  error
   194  	)
   195  
   196  	done := make(chan struct{})
   197  	go func() {
   198  		defer func() { done <- struct{}{} }()
   199  
   200  		//return the quit closure for validation within validateIter
   201  		quitF := func() bool { return quit }
   202  
   203  		//start of invokes
   204  		cc.invokeStartTime = time.Now().UnixNano() / 1000000
   205  
   206  		err = cc.doInvokes(ctxt, chainID, bc, ec, signer, wg, quitF)
   207  
   208  		//end of invokes
   209  		cc.invokeEndTime = time.Now().UnixNano() / 1000000
   210  	}()
   211  
   212  	//we could be done or cancelled or timedout
   213  	select {
   214  	case <-ctxt.Done():
   215  		quit = true
   216  		return nil
   217  	case <-done:
   218  		return err
   219  	case <-time.After(time.Duration(cc.TimeoutToAbortSecs) * time.Second):
   220  		quit = true
   221  		return fmt.Errorf("Aborting due to timeoutout!!")
   222  	}
   223  }
   224  
   225  //validates the invoke iteration for this chaincode
   226  func (cc *CCClient) validateIter(ctxt context.Context, iter int, chainID string, bc common.BroadcastClient, ec pb.EndorserClient, signer msp.SigningIdentity, wg *sync.WaitGroup, quit func() bool) {
   227  	defer wg.Done()
   228  	args := cc.shadowCC.GetQueryArgs(cc.ID, iter)
   229  
   230  	spec := cc.getChaincodeSpec(args)
   231  
   232  	//lets try a few times
   233  	for cc.currQueryIter[iter] = 0; cc.currQueryIter[iter] < cc.NumFinalQueryAttempts; cc.currQueryIter[iter]++ {
   234  		if quit() {
   235  			break
   236  		}
   237  
   238  		var pResp *pb.ProposalResponse
   239  		var err error
   240  		if pResp, err = chaincode.ChaincodeInvokeOrQuery(spec, chainID, false, signer, ec, bc); err != nil {
   241  			cc.queryErrs[iter] = err
   242  			break
   243  		}
   244  
   245  		resp := pResp.Response.Payload
   246  
   247  		if quit() {
   248  			break
   249  		}
   250  
   251  		//if it fails, we try again
   252  		if err = cc.shadowCC.Validate(args, resp); err == nil {
   253  			//appears to have worked
   254  			cc.queryWorked[iter] = true
   255  			cc.queryErrs[iter] = nil
   256  			break
   257  		}
   258  
   259  		//save query error
   260  		cc.queryErrs[iter] = err
   261  
   262  		if quit() {
   263  			break
   264  		}
   265  
   266  		//try again
   267  		if cc.DelayBetweenQueryMs > 0 {
   268  			time.Sleep(time.Duration(cc.DelayBetweenQueryMs) * time.Millisecond)
   269  		}
   270  	}
   271  
   272  	return
   273  }
   274  
   275  //Validate test that was Run. Each successful iteration in the run is validated against
   276  func (cc *CCClient) Validate(ctxt context.Context, chainID string, bc common.BroadcastClient, ec pb.EndorserClient, signer msp.SigningIdentity, wg *sync.WaitGroup) error {
   277  	defer wg.Done()
   278  
   279  	//this will signal inner validators to get out via
   280  	//closure
   281  	var quit bool
   282  
   283  	//use 1 so sender doesn't block (he doesn't care if is was receivd.
   284  	//makes sure goroutine exits)
   285  	done := make(chan struct{}, 1)
   286  	go func() {
   287  		defer func() { done <- struct{}{} }()
   288  
   289  		var innerwg sync.WaitGroup
   290  		innerwg.Add(cc.currentInvokeIter)
   291  
   292  		//initialize for querying
   293  		cc.currQueryIter = make([]int, cc.currentInvokeIter)
   294  		cc.queryWorked = make([]bool, cc.currentInvokeIter)
   295  		cc.queryErrs = make([]error, cc.currentInvokeIter)
   296  
   297  		//give some time for the invokes to commit for this cc
   298  		time.Sleep(time.Duration(cc.WaitAfterInvokeMs) * time.Millisecond)
   299  
   300  		//return the quit closure for validation within validateIter
   301  		quitF := func() bool { return quit }
   302  
   303  		cc.numQueries = cc.shadowCC.GetNumQueries(cc.currentInvokeIter)
   304  
   305  		//try only till successful invoke iterations
   306  		for i := 0; i < cc.numQueries; i++ {
   307  			go func(iter int) {
   308  				cc.validateIter(ctxt, iter, chainID, bc, ec, signer, &innerwg, quitF)
   309  			}(i)
   310  		}
   311  
   312  		//shouldn't block the sender go routine on cleanup
   313  		qDone := make(chan struct{}, 1)
   314  
   315  		//wait for the above queries to be done
   316  		go func() { innerwg.Wait(); qDone <- struct{}{} }()
   317  
   318  		//we could be done or cancelled
   319  		select {
   320  		case <-qDone:
   321  		case <-ctxt.Done():
   322  		}
   323  	}()
   324  
   325  	//we could be done or cancelled or timedout
   326  	select {
   327  	case <-ctxt.Done():
   328  		//we don't know why it was cancelled but it was cancelled
   329  		quit = true
   330  		return nil
   331  	case <-done:
   332  		//for done does not return an err. The query validation stores chaincode errors
   333  		//Only error that's left to handle is timeout error for this chaincode below
   334  		return nil
   335  	case <-time.After(time.Duration(cc.TimeoutToAbortSecs) * time.Second):
   336  		quit = true
   337  		return fmt.Errorf("Aborting due to timeoutout!!")
   338  	}
   339  }
   340  
   341  //Report reports chaincode test execution, iter by iter
   342  func (cc *CCClient) Report(verbose bool, chainID string) {
   343  	fmt.Printf("%s/%s(%d)\n", cc.Name, chainID, cc.ID)
   344  	fmt.Printf("\tNum successful invokes: %d(%d,%d)\n", cc.currentInvokeIter, cc.NumberOfInvokes, cc.overriddenNumInvokes)
   345  	if cc.invokeErr != nil {
   346  		fmt.Printf("\tError on invoke: %s\n", cc.invokeErr)
   347  	}
   348  	//test to see if validate was called (validate alloc the arrays, one of which is queryWorked)
   349  	if cc.queryWorked != nil {
   350  		for i := 0; i < cc.numQueries; i++ {
   351  			fmt.Printf("\tQuery(%d) : succeeded-%t, num trials-%d(%d), error if any(%s)\n", i, cc.queryWorked[i], cc.currQueryIter[i], cc.NumFinalQueryAttempts, cc.queryErrs[i])
   352  		}
   353  	} else {
   354  		fmt.Printf("\tQuery validation appears not have been performed(#invokes-%d). timed out ?\n", cc.currentInvokeIter)
   355  	}
   356  	//total actual time for cc.currentInvokeIter
   357  	invokeTime := cc.invokeEndTime - cc.invokeStartTime - int64(cc.DelayBetweenInvokeMs*(cc.currentInvokeIter-1))
   358  	fmt.Printf("\tTime for invokes(ms): %d\n", invokeTime)
   359  
   360  	fmt.Printf("\tFinal query worked ? %t\n", cc.queryWorked)
   361  }