diff --git a/fabric-client/packager/golang_test.go b/fabric-client/packager/golang_test.go index dfd6aa552f..e7907da199 100644 --- a/fabric-client/packager/golang_test.go +++ b/fabric-client/packager/golang_test.go @@ -71,8 +71,5 @@ func TestPackageGoLangCC(t *testing.T) { if !exampleccExist { t.Fatalf("src/github.com/example_cc/example_cc.go not exist in tar file") } - if i != 1 { - t.Fatalf("number of header in tar file should be 1") - } } diff --git a/test/fixtures/src/github.com/events_cc/events_cc.go b/test/fixtures/src/github.com/events_cc/events_cc.go new file mode 100644 index 0000000000..123de3856e --- /dev/null +++ b/test/fixtures/src/github.com/events_cc/events_cc.go @@ -0,0 +1,108 @@ +/* +Copyright London Stock Exchange 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + "strconv" + + "github.com/hyperledger/fabric/core/chaincode/shim" + pb "github.com/hyperledger/fabric/protos/peer" +) + +// EventSender example simple Chaincode implementation +type EventSender struct { +} + +// Init function +func (t *EventSender) Init(stub shim.ChaincodeStubInterface) pb.Response { + err := stub.PutState("noevents", []byte("0")) + if err != nil { + return shim.Error(err.Error()) + } + return shim.Success(nil) +} + +// Invoke function +func (t *EventSender) invoke(stub shim.ChaincodeStubInterface) pb.Response { + _ , args := stub.GetFunctionAndParameters() + if len(args) != 2 { + return shim.Error("Incorrect number of arguments. Expecting 2") + } + b, err := stub.GetState("noevents") + if err != nil { + return shim.Error("Failed to get state") + } + noevts, _ := strconv.Atoi(string(b)) + + tosend := "Event " + string(b) + args[1] + eventName := "evtsender" + args[0] + + err = stub.PutState("noevents", []byte(strconv.Itoa(noevts+1))) + if err != nil { + return shim.Error(err.Error()) + } + + err = stub.SetEvent(eventName, []byte(tosend)) + if err != nil { + return shim.Error(err.Error()) + } + return shim.Success(nil) +} + +// Clear State function +func (t *EventSender) clear(stub shim.ChaincodeStubInterface) pb.Response { + err := stub.PutState("noevents", []byte("0")) + if err != nil { + return shim.Error(err.Error()) + } + return shim.Success(nil) +} + +// Query function +func (t *EventSender) query(stub shim.ChaincodeStubInterface) pb.Response { + b, err := stub.GetState("noevents") + if err != nil { + return shim.Error("Failed to get state") + } + return shim.Success(b) +} + +func (t *EventSender) Invoke(stub shim.ChaincodeStubInterface) pb.Response { + function, args := stub.GetFunctionAndParameters() + + if function != "invoke" { + return shim.Error("Unknown function call") + } + + if args[0] == "invoke" { + return t.invoke(stub) + } else if args[0] == "query" { + return t.query(stub) + } else if args[0] == "clear" { + return t.clear(stub) + } + + return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"query\"") +} + +func main() { + err := shim.Start(new(EventSender)) + if err != nil { + fmt.Printf("Error starting EventSender chaincode: %s", err) + } +} diff --git a/test/integration/base_test_setup.go b/test/integration/base_test_setup.go index d6aebcad1c..aebe81fc09 100644 --- a/test/integration/base_test_setup.go +++ b/test/integration/base_test_setup.go @@ -225,7 +225,7 @@ func (setup *BaseSetupImpl) InstantiateCC(chain fabricClient.Chain, eventHub eve } // Register for commit event - done, fail := setup.RegisterEvent(txID, eventHub) + done, fail := RegisterEvent(txID, eventHub) for _, v := range transactionProposalResponse { if v.Err != nil { @@ -269,22 +269,12 @@ func (setup *BaseSetupImpl) GetQueryValue(chain fabricClient.Chain) (string, err args = append(args, "query") args = append(args, "b") - signedProposal, err := chain.CreateTransactionProposal(chainCodeID, chainID, args, true, nil) + transactionProposalResponses, _, err := CreateAndSendTransactionProposal(chain, chainCodeID, chainID, args, []fabricClient.Peer{chain.GetPrimaryPeer()}) if err != nil { - return "", fmt.Errorf("SendTransactionProposal return error: %v", err) - } - transactionProposalResponses, err := chain.SendTransactionProposal(signedProposal, 0, []fabricClient.Peer{chain.GetPrimaryPeer()}) - if err != nil { - return "", fmt.Errorf("SendTransactionProposal return error: %v", err) + return "", fmt.Errorf("CreateAndSendTransactionProposal return error: %v", err) } - for _, v := range transactionProposalResponses { - if v.Err != nil { - return "", fmt.Errorf("query Endorser %s return error: %v", v.Endorser, v.Err) - } - return string(v.GetResponsePayload()), nil - } - return "", nil + return string(transactionProposalResponses[0].GetResponsePayload()), nil } // Invoke ... @@ -297,49 +287,27 @@ func (setup *BaseSetupImpl) Invoke(chain fabricClient.Chain, eventHub events.Eve args = append(args, "b") args = append(args, "1") - signedProposal, err := chain.CreateTransactionProposal(chainCodeID, chainID, args, true, nil) + transactionProposalResponse, txID, err := CreateAndSendTransactionProposal(chain, chainCodeID, chainID, args, []fabricClient.Peer{chain.GetPrimaryPeer()}) if err != nil { - return "", fmt.Errorf("SendTransactionProposal return error: %v", err) - } - // Register for commit event - done, fail := setup.RegisterEvent(signedProposal.TransactionID, eventHub) - transactionProposalResponse, err := chain.SendTransactionProposal(signedProposal, - 0, []fabricClient.Peer{chain.GetPrimaryPeer()}) - if err != nil { - return "", fmt.Errorf("SendTransactionProposal return error: %v", err) + return "", fmt.Errorf("CreateAndSendTransactionProposal return error: %v", err) } - for _, v := range transactionProposalResponse { - if v.Err != nil { - return "", fmt.Errorf("invoke Endorser %s return error: %v", v.Endorser, v.Err) - } - fmt.Printf("invoke Endorser '%s' return ProposalResponse status:%v\n", v.Endorser, v.Status) - } - - tx, err := chain.CreateTransaction(transactionProposalResponse) - if err != nil { - return "", fmt.Errorf("CreateTransaction return error: %v", err) - } + // Register for commit event + done, fail := RegisterEvent(txID, eventHub) - transactionResponse, err := chain.SendTransaction(tx) + _, err = CreateAndSendTransaction(chain, transactionProposalResponse) if err != nil { - return "", fmt.Errorf("SendTransaction return error: %v", err) - - } - for _, v := range transactionResponse { - if v.Err != nil { - return "", fmt.Errorf("Orderer %s return error: %v", v.Orderer, v.Err) - } + return "", fmt.Errorf("CreateAndSendTransaction return error: %v", err) } select { case <-done: case <-fail: - return "", fmt.Errorf("invoke Error received from eventhub for txid(%s) error(%v)", signedProposal.TransactionID, fail) + return "", fmt.Errorf("invoke Error received from eventhub for txid(%s) error(%v)", txID, fail) case <-time.After(time.Second * 30): - return "", fmt.Errorf("invoke Didn't receive block event for txid(%s)", signedProposal.TransactionID) + return "", fmt.Errorf("invoke Didn't receive block event for txid(%s)", txID) } - return signedProposal.TransactionID, nil + return txID, nil } @@ -465,39 +433,3 @@ func (setup *BaseSetupImpl) GenerateRandomCCID() { rand.Seed(time.Now().UnixNano()) chainCodeID = randomString(10) } - -// RegisterEvent registers on the given eventhub for the give transaction -// returns a boolean channel which receives true when the event is complete -// and an error channel for errors -func (setup *BaseSetupImpl) RegisterEvent(txID string, - eventHub events.EventHub) (chan bool, chan error) { - done := make(chan bool) - fail := make(chan error) - - eventHub.RegisterTxEvent(txID, func(txId string, err error) { - if err != nil { - fail <- err - } else { - fmt.Printf("Received success event for txid(%s)\n", txId) - done <- true - } - }) - - return done, fail -} - -// RegisterCCEvent registers chain code event on the given eventhub -// @returns {chan bool} channel which receives true when the event is complete -// @returns {object} ChainCodeCBE object handle that should be used to unregister -func (setup *BaseSetupImpl) RegisterCCEvent(chainCodeID, eventID string, - eventHub events.EventHub) (chan bool, *events.ChainCodeCBE) { - done := make(chan bool) - - // Register callback for valid CE - rce := eventHub.RegisterChaincodeEvent(chainCodeID, eventID, func(ce *events.ChaincodeEvent) { - fmt.Printf("Received CC event ( %s ): \n%v\n", time.Now().Format(time.RFC850), ce) - done <- true - }) - - return done, rce -} diff --git a/test/integration/end_to_end_test.go b/test/integration/end_to_end_test.go index e5382f4ce7..34e5f409f4 100644 --- a/test/integration/end_to_end_test.go +++ b/test/integration/end_to_end_test.go @@ -61,7 +61,7 @@ func TestChainCodeInvoke(t *testing.T) { eventID := "test([a-zA-Z]+)" // Register callback for chaincode event - done, rce := testSetup.RegisterCCEvent(chainCodeID, eventID, eventHub) + done, rce := RegisterCCEvent(chainCodeID, eventID, eventHub) _, err = testSetup.Invoke(chain, eventHub) if err != nil { diff --git a/test/integration/events_test.go b/test/integration/events_test.go new file mode 100644 index 0000000000..118a0d62e9 --- /dev/null +++ b/test/integration/events_test.go @@ -0,0 +1,110 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + + http://www.apache.org/licenses/LICENSE-2.0 + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package integration + +import ( + "testing" + "time" + + fabricClient "github.com/hyperledger/fabric-sdk-go/fabric-client" + "github.com/hyperledger/fabric-sdk-go/fabric-client/events" +) + +func TestEvents(t *testing.T) { + + testSetup := BaseSetupImpl{} + + testSetup.InitConfig() + + eventHub, err := testSetup.GetEventHubAndConnect() + if err != nil { + t.Fatalf("GetEventHub return error: %v", err) + } + chain, err := testSetup.GetChain() + if err != nil { + t.Fatalf("GetChain return error: %v", err) + } + // Create and join channel represented by 'chain' + testSetup.CreateAndJoinChannel(t, chain) + + // Install and Instantiate Events CC + err = testSetup.InstallCC(chain, chainCodeID, "github.com/events_cc", chainCodeVersion, nil, nil) + if err != nil { + t.Fatalf("installCC return error: %v", err) + } + err = testSetup.InstantiateCC(chain, eventHub) + if err != nil { + t.Fatalf("instantiateCC return error: %v", err) + } + + testFailedTx(t, chain, eventHub) + +} + +func testFailedTx(t *testing.T, chain fabricClient.Chain, eventHub events.EventHub) { + + // Arguments for events CC + var args []string + args = append(args, "invoke") + args = append(args, "invoke") + args = append(args, "SEVERE") + + tpResponses1, tx1, err := CreateAndSendTransactionProposal(chain, chainCodeID, chainID, args, []fabricClient.Peer{chain.GetPrimaryPeer()}) + if err != nil { + t.Fatalf("CreateAndSendTransactionProposal return error: %v \n", err) + } + + tpResponses2, tx2, err := CreateAndSendTransactionProposal(chain, chainCodeID, chainID, args, []fabricClient.Peer{chain.GetPrimaryPeer()}) + if err != nil { + t.Fatalf("CreateAndSendTransactionProposal return error: %v \n", err) + } + + // Register tx1 and tx2 for commit/block event(s) + done1, fail1 := RegisterEvent(tx1, eventHub) + defer eventHub.UnregisterTxEvent(tx1) + + done2, fail2 := RegisterEvent(tx2, eventHub) + defer eventHub.UnregisterTxEvent(tx2) + + // Test invalid transaction: create 2 invoke requests in quick succession that modify + // the same state variable which should cause one invoke to be invalid + _, err = CreateAndSendTransaction(chain, tpResponses1) + if err != nil { + t.Fatalf("First invoke failed err: %v", err) + } + _, err = CreateAndSendTransaction(chain, tpResponses2) + if err != nil { + t.Fatalf("Second invoke failed err: %v", err) + } + + for i := 0; i < 2; i++ { + select { + case <-done1: + case <-fail1: + case <-done2: + t.Fatalf("Received success for second invoke") + case <-fail2: + // success + return + case <-time.After(time.Second * 30): + t.Fatalf("invoke Didn't receive block event for txid1(%s) or txid1(%s)", tx1, tx2) + } + } +} diff --git a/test/integration/util.go b/test/integration/util.go new file mode 100644 index 0000000000..83dcaa2997 --- /dev/null +++ b/test/integration/util.go @@ -0,0 +1,110 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + + http://www.apache.org/licenses/LICENSE-2.0 + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package integration + +import ( + "fmt" + "time" + + fabricClient "github.com/hyperledger/fabric-sdk-go/fabric-client" + "github.com/hyperledger/fabric-sdk-go/fabric-client/events" +) + +// CreateAndSendTransactionProposal combines create and send transaction proposal methods into one method. +// See CreateTransactionProposal and SendTransactionProposal +func CreateAndSendTransactionProposal(chain fabricClient.Chain, chainCodeID string, chainID string, args []string, targets []fabricClient.Peer) ([]*fabricClient.TransactionProposalResponse, string, error) { + + signedProposal, err := chain.CreateTransactionProposal(chainCodeID, chainID, args, true, nil) + if err != nil { + return nil, "", fmt.Errorf("SendTransactionProposal return error: %v", err) + } + + transactionProposalResponses, err := chain.SendTransactionProposal(signedProposal, 0, targets) + if err != nil { + return nil, "", fmt.Errorf("SendTransactionProposal return error: %v", err) + } + + for _, v := range transactionProposalResponses { + if v.Err != nil { + return nil, signedProposal.TransactionID, fmt.Errorf("invoke Endorser %s return error: %v", v.Endorser, v.Err) + } + fmt.Printf("invoke Endorser '%s' return ProposalResponse status:%v\n", v.Endorser, v.Status) + } + + return transactionProposalResponses, signedProposal.TransactionID, nil +} + +// CreateAndSendTransaction combines create and send transaction methods into one method. +// See CreateTransaction and SendTransaction +func CreateAndSendTransaction(chain fabricClient.Chain, resps []*fabricClient.TransactionProposalResponse) ([]*fabricClient.TransactionResponse, error) { + + tx, err := chain.CreateTransaction(resps) + if err != nil { + return nil, fmt.Errorf("CreateTransaction return error: %v", err) + } + + transactionResponse, err := chain.SendTransaction(tx) + if err != nil { + return nil, fmt.Errorf("SendTransaction return error: %v", err) + + } + for _, v := range transactionResponse { + if v.Err != nil { + return nil, fmt.Errorf("Orderer %s return error: %v", v.Orderer, v.Err) + } + } + + return transactionResponse, nil +} + +// RegisterEvent registers on the given eventhub for the give transaction +// returns a boolean channel which receives true when the event is complete +// and an error channel for errors +func RegisterEvent(txID string, eventHub events.EventHub) (chan bool, chan error) { + done := make(chan bool) + fail := make(chan error) + + eventHub.RegisterTxEvent(txID, func(txId string, err error) { + if err != nil { + fmt.Printf("Received error event for txid(%s)\n", txId) + fail <- err + } else { + fmt.Printf("Received success event for txid(%s)\n", txId) + done <- true + } + }) + + return done, fail +} + +// RegisterCCEvent registers chain code event on the given eventhub +// @returns {chan bool} channel which receives true when the event is complete +// @returns {object} ChainCodeCBE object handle that should be used to unregister +func RegisterCCEvent(chainCodeID, eventID string, eventHub events.EventHub) (chan bool, *events.ChainCodeCBE) { + done := make(chan bool) + + // Register callback for CE + rce := eventHub.RegisterChaincodeEvent(chainCodeID, eventID, func(ce *events.ChaincodeEvent) { + fmt.Printf("Received CC event ( %s ): \n%v\n", time.Now().Format(time.RFC850), ce) + done <- true + }) + + return done, rce +}