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 }