Commit 61affa05 authored by Srinivasan Muralidharan's avatar Srinivasan Muralidharan
Browse files

FAB-1198-rm old pb.Transaction, pb.Block

NOTE - Removing of old proto.Transaction is the cause for
the large change set. It affects chaincode framework and
all users of the framework such as endorser, system chaincodes,
unit tests etc.

Transaction2 is renamed to Transaction.
Response2 is renamed to Response.
Message2 is renamed to Message.

The changes are fully described in
    https://jira.hyperledger.org/browse/FAB-1198



Summary
=======
   . Remove old Transaction and rename Transaction2
   . Cleanup of Chaincode protobuf message
   . Add TxID for SDK and higher layers to optionally
     set (currently errors if not set)

ChaincodeMessage removes QUERY and QUERY_CHAINCODE enums.

Shim interface does not enforce Query or QueryChaincode.

chaincode_example02 and 05 implement Query function via
the Invoke implementation.

The "noop" system chaincode is removed
   . it was using Transaction which is not an endorser
     artifact any longer
   . there are many system chaincodes to that thoroughly
     test sys chaincode functions

Change-Id: Ib77b7e5a6756eac47e888309816076580ae505e7
Signed-off-by: default avatarSrinivasan Muralidharan <muralisr@us.ibm.com>
parent d016edb3
......@@ -48,6 +48,9 @@ func createProposalForChaincode(ccChaincodeDeploymentSpec *pb.ChaincodeDeploymen
ChaincodeID: &pb.ChaincodeID{Name: "lccc"},
CtorMsg: &pb.ChaincodeInput{Args: [][]byte{[]byte("deploy"), []byte("default"), ccDeploymentSpecBytes}}}
lcChaincodeInvocationSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: lcChaincodeSpec}
uuid := createPropsalID()
// make proposal
return putils.CreateChaincodeProposal(lcChaincodeInvocationSpec, creator)
return putils.CreateChaincodeProposal(uuid, lcChaincodeInvocationSpec, creator)
}
/*
Copyright Digital Asset Holdings, LLC 2016 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 noop
import (
"errors"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)
var logger = shim.NewLogger("noop")
type ledgerHandler interface {
GetTransactionByID(txID string) (*pb.Transaction, error)
}
// SystemChaincode is type representing the chaincode
// In general, one should not use vars in memory that can hold state
// across invokes but this is used JUST for MOCKING
type SystemChaincode struct {
mockLedgerH ledgerHandler
}
func (t *SystemChaincode) getLedger() ledgerHandler {
if t.mockLedgerH == nil {
panic("Chaincode is unable to get the ledger.")
} else {
return t.mockLedgerH
}
}
// Init initailizes the system chaincode
func (t *SystemChaincode) Init(stub shim.ChaincodeStubInterface) ([]byte, error) {
logger.SetLevel(shim.LogDebug)
logger.Debugf("NOOP INIT")
return nil, nil
}
// Invoke runs an invocation on the system chaincode
func (t *SystemChaincode) Invoke(stub shim.ChaincodeStubInterface) ([]byte, error) {
args := stub.GetStringArgs()
if len(args) != 1 {
return nil, errors.New("Noop execute operation must have one single argument.")
}
logger.Infof("Executing noop invoke.")
return nil, nil
}
// Query callback representing the query of a chaincode
func (t *SystemChaincode) Query(stub shim.ChaincodeStubInterface) ([]byte, error) {
function, args := stub.GetFunctionAndParameters()
switch function {
case "getTran":
if len(args) < 1 {
return nil, errors.New("getTran operation must include a single argument, the TX hash hex")
}
logger.Infof("Executing NOOP QUERY")
logger.Infof("--> %x", args[0])
var txHashHex = args[0]
var tx, txerr = t.getLedger().GetTransactionByID(txHashHex)
if nil != txerr || nil == tx {
return nil, txerr
}
newCCIS := &pb.ChaincodeInvocationSpec{}
var merr = proto.Unmarshal(tx.Payload, newCCIS)
if nil != merr {
return nil, merr
}
if len(newCCIS.ChaincodeSpec.CtorMsg.Args) < 1 {
return nil, errors.New("The requested transaction is malformed.")
}
var dataInByteForm = newCCIS.ChaincodeSpec.CtorMsg.Args[0]
return dataInByteForm, nil
default:
return nil, errors.New("Unsupported operation")
}
}
/*
Copyright Digital Asset Holdings, LLC 2016 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 noop
import (
"fmt"
"testing"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/core/util"
pb "github.com/hyperledger/fabric/protos/peer"
)
var something = "c29tZXRoaW5n"
func TestMocking(t *testing.T) {
var mockledger, ledger ledgerHandler
mockledger = mockLedger{}
var noop = SystemChaincode{mockledger}
ledger = noop.getLedger()
if mockledger != ledger {
t.Errorf("Mocking functionality of Noop system chaincode does not work.")
}
}
func TestInvokeUnsupported(t *testing.T) {
var noop = SystemChaincode{mockLedger{}}
stub := shim.InitTestStub("unsupported_operation", "arg1", "arg2")
var res, err = noop.Invoke(stub)
if res != nil || err == nil {
t.Errorf("Invoke has to return nil and error when called with unsupported operation!")
}
}
func TestInvokeExecuteNotEnoughArgs(t *testing.T) {
var noop = SystemChaincode{mockLedger{}}
stub := shim.InitTestStub()
var res, err = noop.Invoke(stub)
if res != nil || err == nil {
t.Errorf("Invoke.execute has to indicate error if called with less than one arguments!")
}
}
func TestInvokeExecuteOneArgReturnsNothing(t *testing.T) {
var noop = SystemChaincode{mockLedger{}}
stub := shim.InitTestStub("transaction")
var res, err = noop.Invoke(stub)
if res != nil || err != nil {
t.Errorf("Invoke.execute has to return nil with no error.")
}
}
func TestInvokeExecuteMoreArgsReturnsError(t *testing.T) {
var noop = SystemChaincode{mockLedger{}}
stub := shim.InitTestStub("transaction", "arg1")
var res, err = noop.Invoke(stub)
if res != nil || err == nil {
t.Errorf("Invoke.execute has to return error when called with more than one arguments.")
}
}
func TestQueryUnsupported(t *testing.T) {
var noop = SystemChaincode{mockLedger{}}
stub := shim.InitTestStub("unsupported_operation", "arg1", "arg2")
var res, err = noop.Query(stub)
if res != nil || err == nil {
t.Errorf("Invoke has to return nil and error when called with unsupported operation!")
}
}
func TestQueryGetTranNotEnoughArgs(t *testing.T) {
var noop = SystemChaincode{mockLedger{}}
stub := shim.InitTestStub("getTran")
var res, err = noop.Query(stub)
if res != nil || err == nil {
t.Errorf("Invoke has to return nil and error when called with unsupported operation!")
}
}
func TestQueryGetTranNonExisting(t *testing.T) {
var noop = SystemChaincode{mockLedger{}}
stub := shim.InitTestStub("getTran", "noSuchTX")
res, err := noop.Query(stub)
if res != nil || err == nil {
t.Errorf("Invoke has to return nil when called with a non-existent transaction.")
}
}
func TestQueryGetTranNonExistingWithManyArgs(t *testing.T) {
var noop = SystemChaincode{mockLedger{}}
stub := shim.InitTestStub("getTran", "noSuchTX", "arg2")
res, err := noop.Query(stub)
if res != nil || err == nil {
t.Errorf("Invoke has to return nil when called with a non-existent transaction.")
}
}
func TestQueryGetTranExisting(t *testing.T) {
var noop = SystemChaincode{mockLedger{}}
stub := shim.InitTestStub("getTran", "someTx")
var res, err = noop.Query(stub)
if res == nil || err != nil {
t.Errorf("Invoke has to return a transaction when called with an existing one.")
}
}
type mockLedger struct {
}
func (ml mockLedger) GetTransactionByID(txID string) (*pb.Transaction, error) {
if txID == "noSuchTX" {
return nil, fmt.Errorf("Some error")
}
newCCIS := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{CtorMsg: &pb.ChaincodeInput{Args: util.ToChaincodeArgs("execute", something)}}}
pl, _ := proto.Marshal(newCCIS)
return &pb.Transaction{Payload: pl}, nil
}
......@@ -33,7 +33,6 @@ import (
"github.com/hyperledger/fabric/core/container"
"github.com/hyperledger/fabric/core/container/ccintf"
"github.com/hyperledger/fabric/core/crypto"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/flogging"
pb "github.com/hyperledger/fabric/protos/peer"
......@@ -107,11 +106,11 @@ func (chaincodeSupport *ChaincodeSupport) chaincodeHasBeenLaunched(chaincode str
}
// NewChaincodeSupport creates a new ChaincodeSupport instance
func NewChaincodeSupport(chainname ChainName, getPeerEndpoint func() (*pb.PeerEndpoint, error), userrunsCC bool, ccstartuptimeout time.Duration, secHelper crypto.Peer) *ChaincodeSupport {
func NewChaincodeSupport(chainname ChainName, getPeerEndpoint func() (*pb.PeerEndpoint, error), userrunsCC bool, ccstartuptimeout time.Duration) *ChaincodeSupport {
pnid := viper.GetString("peer.networkId")
pid := viper.GetString("peer.id")
s := &ChaincodeSupport{name: chainname, runningChaincodes: &runningChaincodes{chaincodeMap: make(map[string]*chaincodeRTEnv)}, secHelper: secHelper, peerNetworkID: pnid, peerID: pid}
s := &ChaincodeSupport{name: chainname, runningChaincodes: &runningChaincodes{chaincodeMap: make(map[string]*chaincodeRTEnv)}, peerNetworkID: pnid, peerID: pid}
//initialize global chain
chains[chainname] = s
......@@ -193,7 +192,6 @@ type ChaincodeSupport struct {
ccStartupTimeout time.Duration
chaincodeInstallPath string
userRunsCC bool
secHelper crypto.Peer
peerNetworkID string
peerID string
peerTLS bool
......@@ -273,7 +271,7 @@ func (chaincodeSupport *ChaincodeSupport) deregisterHandler(chaincodehandler *Ha
}
// Based on state of chaincode send either init or ready to move to ready state
func (chaincodeSupport *ChaincodeSupport) sendInitOrReady(context context.Context, txid string, chaincode string, initArgs [][]byte, timeout time.Duration, tx *pb.Transaction, depTx *pb.Transaction) error {
func (chaincodeSupport *ChaincodeSupport) sendInitOrReady(context context.Context, txid string, prop *pb.Proposal, chaincode string, initArgs [][]byte, timeout time.Duration) error {
chaincodeSupport.runningChaincodes.Lock()
//if its in the map, there must be a connected stream...nothing to do
var chrte *chaincodeRTEnv
......@@ -287,7 +285,7 @@ func (chaincodeSupport *ChaincodeSupport) sendInitOrReady(context context.Contex
var notfy chan *pb.ChaincodeMessage
var err error
if notfy, err = chrte.handler.initOrReady(context, txid, initArgs, tx, depTx); err != nil {
if notfy, err = chrte.handler.initOrReady(context, txid, prop, initArgs); err != nil {
return fmt.Errorf("Error sending %s: %s", pb.ChaincodeMessage_INIT, err)
}
if notfy != nil {
......@@ -445,35 +443,30 @@ func (chaincodeSupport *ChaincodeSupport) Stop(context context.Context, cds *pb.
}
// Launch will launch the chaincode if not running (if running return nil) and will wait for handler of the chaincode to get into FSM ready state.
func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, t *pb.Transaction) (*pb.ChaincodeID, *pb.ChaincodeInput, error) {
func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, txid string, prop *pb.Proposal, spec interface{}) (*pb.ChaincodeID, *pb.ChaincodeInput, error) {
//build the chaincode
var cID *pb.ChaincodeID
var cMsg *pb.ChaincodeInput
var cLang pb.ChaincodeSpec_Type
var initargs [][]byte
cds := &pb.ChaincodeDeploymentSpec{}
if t.Type == pb.Transaction_CHAINCODE_DEPLOY {
err := proto.Unmarshal(t.Payload, cds)
if err != nil {
return nil, nil, err
var cds *pb.ChaincodeDeploymentSpec
var ci *pb.ChaincodeInvocationSpec
if cds, _ = spec.(*pb.ChaincodeDeploymentSpec); cds == nil {
if ci, _ = spec.(*pb.ChaincodeInvocationSpec); ci == nil {
panic("Launch should be called with deployment or invocation spec")
}
}
if cds != nil {
cID = cds.ChaincodeSpec.ChaincodeID
cMsg = cds.ChaincodeSpec.CtorMsg
cLang = cds.ChaincodeSpec.Type
initargs = cMsg.Args
} else if t.Type == pb.Transaction_CHAINCODE_INVOKE || t.Type == pb.Transaction_CHAINCODE_QUERY {
ci := &pb.ChaincodeInvocationSpec{}
err := proto.Unmarshal(t.Payload, ci)
if err != nil {
return nil, nil, err
}
} else {
cID = ci.ChaincodeSpec.ChaincodeID
cMsg = ci.ChaincodeSpec.CtorMsg
} else {
chaincodeSupport.runningChaincodes.Unlock()
return nil, nil, fmt.Errorf("invalid transaction type: %d", t.Type)
}
chaincode := cID.Name
chaincodeSupport.runningChaincodes.Lock()
var chrte *chaincodeRTEnv
......@@ -496,21 +489,7 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, t *pb.
}
chaincodeSupport.runningChaincodes.Unlock()
var depTx *pb.Transaction
//extract depTx so we can initialize hander.deployTXSecContext
//we need it only after container is launched and only if this is not a deploy tx
//NOTE: ideally this section should be moved before just before sendInitOrReady where
// where we need depTx. However, as we don't check for ExecuteTransactions failure
// in consensus/helper, the following race is not resolved:
// 1) deploy creates image
// 2) query launches chaincode
// 3) deploy returns "premature execution" error
// 4) error ignored and deploy committed
// 5) query successfully retrives committed tx and calls sendInitOrReady
// See issue #710
if t.Type != pb.Transaction_CHAINCODE_DEPLOY {
if cds == nil {
if chaincodeSupport.userRunsCC {
chaincodeLogger.Error("You are attempting to perform an action other than Deploy on Chaincode that is not ready and you are in developer mode. Did you forget to Deploy your chaincode?")
}
......@@ -521,7 +500,7 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, t *pb.
var depPayload []byte
//hopefully we are restarting from existing image and the deployed transaction exists
depPayload, err = GetCDSFromLCCC(context, string(DefaultChain), chaincode)
depPayload, err = GetCDSFromLCCC(context, txid, prop, string(DefaultChain), chaincode)
if err != nil {
return cID, cMsg, fmt.Errorf("Could not get deployment transaction from LCCC for %s - %s", chaincode, err)
}
......@@ -529,6 +508,7 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, t *pb.
return cID, cMsg, fmt.Errorf("failed to get deployment payload %s - %s", chaincode, err)
}
cds = &pb.ChaincodeDeploymentSpec{}
//Get lang from original deployment
err = proto.Unmarshal(depPayload, cds)
if err != nil {
......@@ -542,7 +522,7 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, t *pb.
//launch container if it is a System container or not in dev mode
if (!chaincodeSupport.userRunsCC || cds.ExecEnv == pb.ChaincodeDeploymentSpec_SYSTEM) && (chrte == nil || chrte.handler == nil) {
var targz io.Reader = bytes.NewBuffer(cds.CodePackage)
_, err = chaincodeSupport.launchAndWaitForRegister(context, cds, cID, t.Txid, cLang, targz)
_, err = chaincodeSupport.launchAndWaitForRegister(context, cds, cID, txid, cLang, targz)
if err != nil {
chaincodeLogger.Errorf("launchAndWaitForRegister failed %s", err)
return cID, cMsg, err
......@@ -551,7 +531,7 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, t *pb.
if err == nil {
//send init (if (args)) and wait for ready state
err = chaincodeSupport.sendInitOrReady(context, t.Txid, chaincode, initargs, chaincodeSupport.ccStartupTimeout, t, depTx)
err = chaincodeSupport.sendInitOrReady(context, txid, prop, chaincode, initargs, chaincodeSupport.ccStartupTimeout)
if err != nil {
chaincodeLogger.Errorf("sending init failed(%s)", err)
err = fmt.Errorf("Failed to init chaincode(%s)", err)
......@@ -568,11 +548,6 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, t *pb.
return cID, cMsg, err
}
// getSecHelper returns the security help set from NewChaincodeSupport
func (chaincodeSupport *ChaincodeSupport) getSecHelper() crypto.Peer {
return chaincodeSupport.secHelper
}
//getVMType - just returns a string for now. Another possibility is to use a factory method to
//return a VM executor
func (chaincodeSupport *ChaincodeSupport) getVMType(cds *pb.ChaincodeDeploymentSpec) (string, error) {
......@@ -583,19 +558,10 @@ func (chaincodeSupport *ChaincodeSupport) getVMType(cds *pb.ChaincodeDeploymentS
}
// Deploy deploys the chaincode if not in development mode where user is running the chaincode.
func (chaincodeSupport *ChaincodeSupport) Deploy(context context.Context, t *pb.Transaction) (*pb.ChaincodeDeploymentSpec, error) {
//build the chaincode
cds := &pb.ChaincodeDeploymentSpec{}
err := proto.Unmarshal(t.Payload, cds)
if err != nil {
return nil, err
}
func (chaincodeSupport *ChaincodeSupport) Deploy(context context.Context, cds *pb.ChaincodeDeploymentSpec) (*pb.ChaincodeDeploymentSpec, error) {
cID := cds.ChaincodeSpec.ChaincodeID
cLang := cds.ChaincodeSpec.Type
chaincode := cID.Name
if err != nil {
return cds, err
}
if chaincodeSupport.userRunsCC {
chaincodeLogger.Debug("user runs chaincode, not deploying chaincode")
......@@ -652,17 +618,8 @@ func createTransactionMessage(txid string, cMsg *pb.ChaincodeInput) (*pb.Chainco
return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: txid}, nil
}
// createQueryMessage creates a query message.
func createQueryMessage(txid string, cMsg *pb.ChaincodeInput) (*pb.ChaincodeMessage, error) {
payload, err := proto.Marshal(cMsg)
if err != nil {
return nil, err
}
return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY, Payload: payload, Txid: txid}, nil
}
// Execute executes a transaction and waits for it to complete until a timeout value.
func (chaincodeSupport *ChaincodeSupport) Execute(ctxt context.Context, chaincode string, msg *pb.ChaincodeMessage, timeout time.Duration, tx *pb.Transaction) (*pb.ChaincodeMessage, error) {
func (chaincodeSupport *ChaincodeSupport) Execute(ctxt context.Context, chaincode string, msg *pb.ChaincodeMessage, timeout time.Duration, prop *pb.Proposal) (*pb.ChaincodeMessage, error) {
chaincodeSupport.runningChaincodes.Lock()
//we expect the chaincode to be running... sanity check
chrte, ok := chaincodeSupport.chaincodeHasBeenLaunched(chaincode)
......@@ -675,13 +632,13 @@ func (chaincodeSupport *ChaincodeSupport) Execute(ctxt context.Context, chaincod
var notfy chan *pb.ChaincodeMessage
var err error
if notfy, err = chrte.handler.sendExecuteMessage(ctxt, msg, tx); err != nil {
if notfy, err = chrte.handler.sendExecuteMessage(ctxt, msg, prop); err != nil {
return nil, fmt.Errorf("Error sending %s: %s", msg.Type.String(), err)
}
var ccresp *pb.ChaincodeMessage
select {
case ccresp = <-notfy:
//response is sent to user or calling chaincode. ChaincodeMessage_ERROR and ChaincodeMessage_QUERY_ERROR
//response is sent to user or calling chaincode. ChaincodeMessage_ERROR
//are typically treated as error
case <-time.After(timeout):
err = fmt.Errorf("Timeout expired while executing transaction")
......
......@@ -21,37 +21,34 @@ import (
"fmt"
"github.com/hyperledger/fabric/core/util"
pb "github.com/hyperledger/fabric/protos/peer"
)
//create a Transactions - this has to change to Proposal when we move chaincode to use Proposals
func createTx(typ pb.Transaction_Type, ccname string, args [][]byte) (*pb.Transaction, error) {
var tx *pb.Transaction
//create a chaincode invocation spec
func createCIS(ccname string, args [][]byte) (*pb.ChaincodeInvocationSpec, error) {
var err error
uuid := util.GenerateUUID()
spec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: 1, ChaincodeID: &pb.ChaincodeID{Name: ccname}, CtorMsg: &pb.ChaincodeInput{Args: args}}}
tx, err = pb.NewChaincodeExecute(spec, uuid, typ)
if nil != err {
return nil, err
}
return tx, nil
return spec, nil
}
func GetCDSFromLCCC(ctxt context.Context, chainID string, chaincodeID string) ([]byte, error) {
payload, _, err := ExecuteChaincode(ctxt, pb.Transaction_CHAINCODE_INVOKE, string(DefaultChain), "lccc", [][]byte{[]byte("getdepspec"), []byte(chainID), []byte(chaincodeID)})
// GetCDSFromLCCC gets chaincode deployment spec from LCCC
func GetCDSFromLCCC(ctxt context.Context, txid string, prop *pb.Proposal, chainID string, chaincodeID string) ([]byte, error) {
payload, _, err := ExecuteChaincode(ctxt, txid, prop, string(DefaultChain), "lccc", [][]byte{[]byte("getdepspec"), []byte(chainID), []byte(chaincodeID)})
return payload, err
}
// ExecuteChaincode executes a given chaincode given chaincode name and arguments
func ExecuteChaincode(ctxt context.Context, typ pb.Transaction_Type, chainname string, ccname string, args [][]byte) ([]byte, *pb.ChaincodeEvent, error) {
var tx *pb.Transaction
func ExecuteChaincode(ctxt context.Context, txid string, prop *pb.Proposal, chainname string, ccname string, args [][]byte) ([]byte, *pb.ChaincodeEvent, error) {
var spec *pb.ChaincodeInvocationSpec
var err error
var b []byte
var ccevent *pb.ChaincodeEvent
tx, err = createTx(typ, ccname, args)
b, ccevent, err = Execute(ctxt, GetChain(ChainName(chainname)), tx)
spec, err = createCIS(ccname, args)
b, ccevent, err = Execute(ctxt, GetChain(ChainName(chainname)), txid, prop, spec)
if err != nil {
return nil, nil, fmt.Errorf("Error deploying chaincode: %s", err)
}
......
......@@ -28,31 +28,29 @@ import (
)
//Execute - execute transaction or a query
func Execute(ctxt context.Context, chain *ChaincodeSupport, t *pb.Transaction) ([]byte, *pb.ChaincodeEvent, error) {
func Execute(ctxt context.Context, chain *ChaincodeSupport, txid string, prop *pb.Proposal, spec interface{}) ([]byte, *pb.ChaincodeEvent, error) {
var err error
if secHelper := chain.getSecHelper(); nil != secHelper {
var err error
t, err = secHelper.TransactionPreExecution(t)
// Note that t is now decrypted and is a deep clone of the original input t
if nil != err {
return nil, nil, err
var cds *pb.ChaincodeDeploymentSpec
var ci *pb.ChaincodeInvocationSpec
if cds, _ = spec.(*pb.ChaincodeDeploymentSpec); cds == nil {
if ci, _ = spec.(*pb.ChaincodeInvocationSpec); ci == nil {
panic("Execute should be called with deployment or invocation spec")
}
}
if t.Type == pb.Transaction_CHAINCODE_DEPLOY {
_, err := chain.Deploy(ctxt, t)
if cds != nil {
_, err := chain.Deploy(ctxt, cds)
if err != nil {
return nil, nil, fmt.Errorf("Failed to deploy chaincode spec(%s)", err)
}
_, _, err = chain.Launch(ctxt, t)
_, _, err = chain.Launch(ctxt, txid, prop, cds)
if err != nil {
return nil, nil, fmt.Errorf("%s", err)
}
} else if t.Type == pb.Transaction_CHAINCODE_INVOKE || t.Type == pb.Transaction_CHAINCODE_QUERY {
} else {
//will launch if necessary (and wait for ready)
cID, cMsg, err := chain.Launch(ctxt, t)
cID, cMsg, err := chain.Launch(ctxt, txid, prop, ci)
if err != nil {
return nil, nil, fmt.Errorf("Failed to launch chaincode spec(%s)", err)
}
......@@ -72,81 +70,38 @@ func Execute(ctxt context.Context, chain *ChaincodeSupport, t *pb.Transaction) (
}
var ccMsg *pb.ChaincodeMessage
if t.Type == pb.Transaction_CHAINCODE_INVOKE {
ccMsg, err = createTransactionMessage(t.Txid, cMsg)
if err != nil {
return nil, nil, fmt.Errorf("Failed to transaction message(%s)", err)
}
} else {
ccMsg, err = createQueryMessage(t.Txid, cMsg)
if err != nil {
return nil, nil, fmt.Errorf("Failed to query message(%s)", err)
}
ccMsg, err = createTransactionMessage(txid, cMsg)
if err != nil {
return nil, nil, fmt.Errorf("Failed to transaction message(%s)", err)
}
resp, err := chain.Execute(ctxt, chaincode, ccMsg, timeout, t)
resp, err := chain.Execute(ctxt, chaincode, ccMsg, timeout, prop)
if err != nil {
// Rollback transaction
return nil, nil, fmt.Errorf("Failed to execute transaction or query(%s)", err)
} else if resp == nil {
// Rollback transaction
return nil, nil, fmt.Errorf("Failed to receive a response for (%s)", t.Txid)
return nil, nil, fmt.Errorf("Failed to receive a response for (%s)", txid)
} else {
if resp.ChaincodeEvent != nil {
resp.ChaincodeEvent.ChaincodeID = chaincode
resp.ChaincodeEvent.TxID = t.Txid
resp.ChaincodeEvent.TxID = txid
}