Skip to content

Commit

Permalink
FAB-1457 Change return value of chaincode
Browse files Browse the repository at this point in the history
Replace return value of chaincode form ([]byte, error) to pb.Response.
So that we can define different kinds of errors. More detail could be
seen at https://jira.hyperledger.org/browse/FAB-1457. Now chaincode
result will pass through shim/chaincode_spoort without converting.
This commit define basic Status Code(200 for success and 500 for error).
More code defination and handle logic will be added in following commit.
Change chaincode examples interfaces and use basic code(200/500).

Change-Id: I2ace7f4f654d343874274c26847f0dac91050d26
Signed-off-by: jiangyaoguo <jiangyaoguo@gmail.com>
  • Loading branch information
jiangyaoguo committed Jan 24, 2017
1 parent 230f3cc commit a5e2d2b
Show file tree
Hide file tree
Showing 52 changed files with 976 additions and 771 deletions.
2 changes: 1 addition & 1 deletion core/chaincode/ccproviderimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (c *ccProviderImpl) GetCCValidationInfoFromLCCC(ctxt context.Context, txid
}

// ExecuteChaincode executes the chaincode specified in the context with the specified arguments
func (c *ccProviderImpl) ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) ([]byte, *peer.ChaincodeEvent, error) {
func (c *ccProviderImpl) ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) (*peer.Response, *peer.ChaincodeEvent, error) {
return ExecuteChaincode(ctxt, cccid.(*ccProviderContextImpl).ctx, args)
}

Expand Down
10 changes: 10 additions & 0 deletions core/chaincode/chaincode_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"strings"

"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/core/container"
"github.com/hyperledger/fabric/core/container/ccintf"
"github.com/hyperledger/fabric/core/ledger"
Expand Down Expand Up @@ -340,6 +341,15 @@ func (chaincodeSupport *ChaincodeSupport) sendInitOrReady(context context.Contex
if ccMsg.Type == pb.ChaincodeMessage_ERROR {
err = fmt.Errorf("Error initializing container %s: %s", canName, string(ccMsg.Payload))
}
if ccMsg.Type == pb.ChaincodeMessage_COMPLETED {
res := &pb.Response{}
_ = proto.Unmarshal(ccMsg.Payload, res)
if res.Status != shim.OK {
err = fmt.Errorf("Error initializing container %s: %s", canName, string(res.Message))
}
// TODO
// return res so that endorser can anylyze it.
}
case <-time.After(timeout):
err = fmt.Errorf("Timeout expired while executing send init message")
}
Expand Down
29 changes: 21 additions & 8 deletions core/chaincode/chaincodeexec.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)

Expand All @@ -40,18 +41,28 @@ func createCIS(ccname string, args [][]byte) (*pb.ChaincodeInvocationSpec, error
func GetCDSFromLCCC(ctxt context.Context, txid string, prop *pb.Proposal, chainID string, chaincodeID string) ([]byte, error) {
version := util.GetSysCCVersion()
cccid := NewCCContext(chainID, "lccc", version, txid, true, prop)
payload, _, err := ExecuteChaincode(ctxt, cccid, [][]byte{[]byte("getdepspec"), []byte(chainID), []byte(chaincodeID)})
return payload, err
res, _, err := ExecuteChaincode(ctxt, cccid, [][]byte{[]byte("getdepspec"), []byte(chainID), []byte(chaincodeID)})
if err != nil {
return nil, fmt.Errorf("Execute getdepspec(%s, %s) of LCCC error: %s", chainID, chaincodeID, err)
}
if res.Status != shim.OK {
return nil, fmt.Errorf("Get ChaincodeDeploymentSpec for %s/%s from LCCC error: %s", chaincodeID, chainID, res.Message)
}

return res.Payload, nil
}

// GetChaincodeDataFromLCCC gets chaincode data from LCCC given name
func GetChaincodeDataFromLCCC(ctxt context.Context, txid string, prop *pb.Proposal, chainID string, chaincodeID string) (*ChaincodeData, error) {
version := util.GetSysCCVersion()
cccid := NewCCContext(chainID, "lccc", version, txid, true, prop)
payload, _, err := ExecuteChaincode(ctxt, cccid, [][]byte{[]byte("getccdata"), []byte(chainID), []byte(chaincodeID)})
res, _, err := ExecuteChaincode(ctxt, cccid, [][]byte{[]byte("getccdata"), []byte(chainID), []byte(chaincodeID)})
if err == nil {
if res.Status != shim.OK {
return nil, fmt.Errorf("%s", res.Message)
}
cd := &ChaincodeData{}
err = proto.Unmarshal(payload, cd)
err = proto.Unmarshal(res.Payload, cd)
if err != nil {
return nil, err
}
Expand All @@ -62,16 +73,18 @@ func GetChaincodeDataFromLCCC(ctxt context.Context, txid string, prop *pb.Propos
}

// ExecuteChaincode executes a given chaincode given chaincode name and arguments
func ExecuteChaincode(ctxt context.Context, cccid *CCContext, args [][]byte) ([]byte, *pb.ChaincodeEvent, error) {
func ExecuteChaincode(ctxt context.Context, cccid *CCContext, args [][]byte) (*pb.Response, *pb.ChaincodeEvent, error) {
var spec *pb.ChaincodeInvocationSpec
var err error
var b []byte
var res *pb.Response
var ccevent *pb.ChaincodeEvent

spec, err = createCIS(cccid.Name, args)
b, ccevent, err = Execute(ctxt, cccid, spec)
res, ccevent, err = Execute(ctxt, cccid, spec)
if err != nil {
chaincodeLogger.Errorf("Error executing chaincode: %s", err)
return nil, nil, fmt.Errorf("Error executing chaincode: %s", err)
}
return b, ccevent, err

return res, ccevent, err
}
51 changes: 25 additions & 26 deletions core/chaincode/configer.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ limitations under the License.
package chaincode

import (
"errors"
"fmt"

"github.com/op/go-logging"

"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/core/peer"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"
)

Expand All @@ -50,10 +50,9 @@ const (
// Init is called once per chain when the chain is created.
// This allows the chaincode to initialize any variables on the ledger prior
// to any transaction execution on the chain.
func (e *PeerConfiger) Init(stub shim.ChaincodeStubInterface) ([]byte, error) {
cnflogger.Info("Init CSCC")

return nil, nil
func (e *PeerConfiger) Init(stub shim.ChaincodeStubInterface) pb.Response {
logger.Info("Init CSCC")
return shim.Success(nil)
}

// Invoke is called for the following:
Expand All @@ -66,11 +65,11 @@ func (e *PeerConfiger) Init(stub shim.ChaincodeStubInterface) ([]byte, error) {
// # args[1] is a configuration Block if args[0] is JoinChain or
// UpdateConfigBlock; otherwise it is the chain id
// TODO: Improve the scc interface to avoid marshal/unmarshal args
func (e *PeerConfiger) Invoke(stub shim.ChaincodeStubInterface) ([]byte, error) {
func (e *PeerConfiger) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
args := stub.GetArgs()

if len(args) < 2 {
return nil, fmt.Errorf("Incorrect number of arguments, %d", len(args))
return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args)))
}
fname := string(args[0])

Expand All @@ -86,72 +85,72 @@ func (e *PeerConfiger) Invoke(stub shim.ChaincodeStubInterface) ([]byte, error)
return updateConfigBlock(args[1])
}

return nil, fmt.Errorf("Requested function %s not found.", fname)
return shim.Error(fmt.Sprintf("Requested function %s not found.", fname))
}

// joinChain will join the specified chain in the configuration block.
// Since it is the first block, it is the genesis block containing configuration
// for this chain, so we want to update the Chain object with this info
func joinChain(blockBytes []byte) ([]byte, error) {
func joinChain(blockBytes []byte) pb.Response {
if blockBytes == nil {
return nil, fmt.Errorf("Genesis block must not be nil.")
return shim.Error("Genesis block must not be nil.")
}

block, err := utils.GetBlockFromBlockBytes(blockBytes)
if err != nil {
return nil, fmt.Errorf("Failed to reconstruct the genesis block, %s", err)
return shim.Error(fmt.Sprintf("Failed to reconstruct the genesis block, %s", err))
}

if err = peer.CreateChainFromBlock(block); err != nil {
return nil, err
return shim.Error(err.Error())
}

chainID, err := utils.GetChainIDFromBlock(block)
if err != nil {
return nil, fmt.Errorf("Failed to get the chain ID from the configuration block, %s", err)
return shim.Error(fmt.Sprintf("Failed to get the chain ID from the configuration block, %s", err))
}

if err = peer.CreateDeliveryService(chainID); err != nil {
return nil, err
return shim.Error(err.Error())
}

return []byte("200"), nil
return shim.Success(nil)
}

func updateConfigBlock(blockBytes []byte) ([]byte, error) {
func updateConfigBlock(blockBytes []byte) pb.Response {
if blockBytes == nil {
return nil, errors.New("Configuration block must not be nil.")
return shim.Error("Configuration block must not be nil.")
}
block, err := utils.GetBlockFromBlockBytes(blockBytes)
if err != nil {
return nil, fmt.Errorf("Failed to reconstruct the configuration block, %s", err)
return shim.Error(fmt.Sprintf("Failed to reconstruct the configuration block, %s", err))
}
chainID, err := utils.GetChainIDFromBlock(block)
if err != nil {
return nil, fmt.Errorf("Failed to get the chain ID from the configuration block, %s", err)
return shim.Error(fmt.Sprintf("Failed to get the chain ID from the configuration block, %s", err))
}

if err := peer.SetCurrConfigBlock(block, chainID); err != nil {
return nil, err
return shim.Error(err.Error())
}

return []byte("200"), nil
return shim.Success(nil)
}

// Return the current configuration block for the specified chainID. If the
// peer doesn't belong to the chain, return error
func getConfigBlock(chainID []byte) ([]byte, error) {
func getConfigBlock(chainID []byte) pb.Response {
if chainID == nil {
return nil, errors.New("ChainID must not be nil.")
return shim.Error("ChainID must not be nil.")
}
block := peer.GetCurrConfigBlock(string(chainID))
if block == nil {
return nil, fmt.Errorf("Unknown chain ID, %s", string(chainID))
return shim.Error(fmt.Sprintf("Unknown chain ID, %s", string(chainID)))
}
blockBytes, err := utils.Marshal(block)
if err != nil {
return nil, err
return shim.Error(err.Error())
}

return blockBytes, nil
return shim.Success(blockBytes)
}
24 changes: 11 additions & 13 deletions core/chaincode/configer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ func TestConfigerInit(t *testing.T) {
e := new(PeerConfiger)
stub := shim.NewMockStub("PeerConfiger", e)

if _, err := stub.MockInit("1", nil); err != nil {
fmt.Println("Init failed", err)
if res := stub.MockInit("1", nil); res.Status != shim.OK {
fmt.Println("Init failed", string(res.Message))
t.FailNow()
}
}
Expand Down Expand Up @@ -74,7 +74,7 @@ func TestConfigerInvokeJoinChainMissingParams(t *testing.T) {
setupEndpoint(t)
// Failed path: Not enough parameters
args := [][]byte{[]byte("JoinChain")}
if _, err := stub.MockInvoke("1", args); err == nil {
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
t.Fatalf("cscc invoke JoinChain should have failed with invalid number of args: %v", args)
}
}
Expand All @@ -91,8 +91,7 @@ func TestConfigerInvokeJoinChainWrongParams(t *testing.T) {

// Failed path: wrong parameter type
args := [][]byte{[]byte("JoinChain"), []byte("action")}
if _, err := stub.MockInvoke("1", args); err == nil {
fmt.Println("Invoke", args, "failed", err)
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
t.Fatalf("cscc invoke JoinChain should have failed with null genesis block. args: %v", args)
}
}
Expand Down Expand Up @@ -124,7 +123,7 @@ func TestConfigerInvokeJoinChainCorrectParams(t *testing.T) {
t.Fatalf("cscc invoke JoinChain failed because invalid block")
}
args := [][]byte{[]byte("JoinChain"), blockBytes}
if _, err = stub.MockInvoke("1", args); err != nil {
if res := stub.MockInvoke("1", args); res.Status != shim.OK {
t.Fatalf("cscc invoke JoinChain failed with: %v", err)
}

Expand All @@ -135,7 +134,7 @@ func TestConfigerInvokeJoinChainCorrectParams(t *testing.T) {
t.Fatalf("cscc invoke JoinChain failed with: %v", err)
}
args = [][]byte{[]byte("GetConfigBlock"), []byte(chainID)}
if _, err = stub.MockInvoke("1", args); err != nil {
if res := stub.MockInvoke("1", args); res.Status != shim.OK {
t.Fatalf("cscc invoke GetConfigBlock failed with: %v", err)
}
}
Expand All @@ -149,14 +148,13 @@ func TestConfigerInvokeUpdateConfigBlock(t *testing.T) {

// Failed path: Not enough parameters
args := [][]byte{[]byte("UpdateConfigBlock")}
if _, err := stub.MockInvoke("1", args); err == nil {
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
t.Fatalf("cscc invoke UpdateConfigBlock should have failed with invalid number of args: %v", args)
}

// Failed path: wrong parameter type
args = [][]byte{[]byte("UpdateConfigBlock"), []byte("action")}
if _, err := stub.MockInvoke("1", args); err == nil {
fmt.Println("Invoke", args, "failed", err)
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
t.Fatalf("cscc invoke UpdateConfigBlock should have failed with null genesis block - args: %v", args)
}

Expand All @@ -166,8 +164,8 @@ func TestConfigerInvokeUpdateConfigBlock(t *testing.T) {
t.Fatalf("cscc invoke UpdateConfigBlock failed because invalid block")
}
args = [][]byte{[]byte("UpdateConfigBlock"), blockBytes}
if _, err := stub.MockInvoke("1", args); err != nil {
t.Fatalf("cscc invoke UpdateConfigBlock failed with: %v", err)
if res := stub.MockInvoke("1", args); res.Status != shim.OK {
t.Fatalf("cscc invoke UpdateConfigBlock failed with: %v", res.Message)
}

// Query the configuration block
Expand All @@ -177,7 +175,7 @@ func TestConfigerInvokeUpdateConfigBlock(t *testing.T) {
t.Fatalf("cscc invoke UpdateConfigBlock failed with: %v", err)
}
args = [][]byte{[]byte("GetConfigBlock"), []byte(chainID)}
if _, err := stub.MockInvoke("1", args); err != nil {
if res := stub.MockInvoke("1", args); res.Status != shim.OK {
t.Fatalf("cscc invoke GetConfigBlock failed with: %v", err)
}

Expand Down
38 changes: 33 additions & 5 deletions core/chaincode/exectransaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ import (
"fmt"
"time"

"github.com/golang/protobuf/proto"
"golang.org/x/net/context"

"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/events/producer"
pb "github.com/hyperledger/fabric/protos/peer"
)

//Execute - execute proposal
func Execute(ctxt context.Context, cccid *CCContext, spec interface{}) ([]byte, *pb.ChaincodeEvent, error) {
//Execute - execute proposal, return original response of chaincode
func Execute(ctxt context.Context, cccid *CCContext, spec interface{}) (*pb.Response, *pb.ChaincodeEvent, error) {
var err error
var cds *pb.ChaincodeDeploymentSpec
var ci *pb.ChaincodeInvocationSpec
Expand Down Expand Up @@ -82,6 +84,11 @@ func Execute(ctxt context.Context, cccid *CCContext, spec interface{}) ([]byte,
} else if resp == nil {
// Rollback transaction
return nil, nil, fmt.Errorf("Failed to receive a response for (%s)", cccid.TxID)
}
res := &pb.Response{}
unmarshalErr := proto.Unmarshal(resp.Payload, res)
if unmarshalErr != nil {
return nil, nil, fmt.Errorf("Failed to unmarshal response for (%s): %s", cccid.TxID, unmarshalErr)
} else {
if resp.ChaincodeEvent != nil {
resp.ChaincodeEvent.ChaincodeID = cccid.Name
Expand All @@ -90,16 +97,37 @@ func Execute(ctxt context.Context, cccid *CCContext, spec interface{}) ([]byte,

if resp.Type == pb.ChaincodeMessage_COMPLETED {
// Success
return resp.Payload, resp.ChaincodeEvent, nil
return res, resp.ChaincodeEvent, nil
} else if resp.Type == pb.ChaincodeMessage_ERROR {
// Rollback transaction
return nil, resp.ChaincodeEvent, fmt.Errorf("Transaction returned with failure: %s", string(resp.Payload))
}
return resp.Payload, nil, fmt.Errorf("receive a response for (%s) but in invalid state(%d)", cccid.TxID, resp.Type)
return res, nil, fmt.Errorf("receive a response for (%s) but in invalid state(%d)", cccid.TxID, resp.Type)
}

}
return nil, nil, err
return &pb.Response{Status: shim.OK, Payload: nil}, nil, err
}

// ExecuteWithErrorFilter is similar to Execute, but filters error contained in chaincode response and returns Payload of response only.
// Mostly used by unit-test.
func ExecuteWithErrorFilter(ctxt context.Context, cccid *CCContext, spec interface{}) ([]byte, *pb.ChaincodeEvent, error) {
res, event, err := Execute(ctxt, cccid, spec)
if err != nil {
chaincodeLogger.Errorf("ExecuteWithErrorFilter %s error: %s", cccid.Name, err)
return nil, nil, err
}

if res == nil {
chaincodeLogger.Errorf("ExecuteWithErrorFilter %s get nil response without error", cccid.Name)
return nil, nil, err
}

if res.Status != shim.OK {
return nil, nil, fmt.Errorf("%s", res.Message)
}

return res.Payload, event, nil
}

// GetSecureContext returns the security context from the context object or error
Expand Down
Loading

2 comments on commit a5e2d2b

@wilsonhuangws
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has the CreateTable method been removed from version 1.0?

@mastersingh24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WillsonKuang - yes. BTW - we generally track issues is jira.hyperledger.org these days or over on chat.hyperledger.org

Please sign in to comment.