Commit 0a03aad9 authored by Christopher Ferris's avatar Christopher Ferris Committed by Gerrit Code Review
Browse files

Merge "chaincode life-cycle system chaincode for a chain" into feature/convergence

parents 5d76c5b0 a3687a1f
...@@ -21,7 +21,7 @@ Feature: Endorser ...@@ -21,7 +21,7 @@ Feature: Endorser
Scenario Outline: Basic deploy endorsement for chaincode through GRPC to multiple endorsers Scenario Outline: Basic deploy endorsement for chaincode through GRPC to multiple endorsers
Given we compose "<ComposeFile>" Given we compose "<ComposeFile>"
And I wait "1" seconds And I wait "5" seconds
And I register with CA supplying username "binhn" and secret "7avZQLwcUe9q" on peers: And I register with CA supplying username "binhn" and secret "7avZQLwcUe9q" on peers:
| vp0 | | vp0 |
...@@ -29,7 +29,7 @@ Feature: Endorser ...@@ -29,7 +29,7 @@ Feature: Endorser
| funcName | arg1 | arg2 | arg3 | arg4 | | funcName | arg1 | arg2 | arg3 | arg4 |
| init | a | 100 | b | 200 | | init | a | 100 | b | 200 |
And user "binhn" creates a deployment proposal "proposal1" using chaincode spec "cc_spec" And user "binhn" creates a deployment proposal "proposal1" using chaincode spec "cc_spec"
And user "binhn" sends proposal "proposal1" to endorsers with timeout of "2" seconds: And user "binhn" sends proposal "proposal1" to endorsers with timeout of "20" seconds:
| vp0 | vp1 | vp2 | vp3 | | vp0 | vp1 | vp2 | vp3 |
And user "binhn" stores their last result as "proposal1Responses" And user "binhn" stores their last result as "proposal1Responses"
Then user "binhn" expects proposal responses "proposal1Responses" with status "200" from endorsers: Then user "binhn" expects proposal responses "proposal1Responses" with status "200" from endorsers:
...@@ -54,7 +54,7 @@ Feature: Endorser ...@@ -54,7 +54,7 @@ Feature: Endorser
| init | a | 100 | b | 200 | | init | a | 100 | b | 200 |
And user "binhn" sets ESCC to "my_escc" for chaincode spec "cc_spec" And user "binhn" sets ESCC to "my_escc" for chaincode spec "cc_spec"
And user "binhn" creates a deployment proposal "proposal1" using chaincode spec "cc_spec" And user "binhn" creates a deployment proposal "proposal1" using chaincode spec "cc_spec"
And user "binhn" sends proposal "proposal1" to endorsers with timeout of "2" seconds: And user "binhn" sends proposal "proposal1" to endorsers with timeout of "20" seconds:
| vp0 | vp1 | vp2 | vp3 | | vp0 | vp1 | vp2 | vp3 |
And user "binhn" stores their last result as "proposal1Responses" And user "binhn" stores their last result as "proposal1Responses"
Then user "binhn" expects proposal responses "proposal1Responses" with status "200" from endorsers: Then user "binhn" expects proposal responses "proposal1Responses" with status "200" from endorsers:
...@@ -77,7 +77,7 @@ Feature: Endorser ...@@ -77,7 +77,7 @@ Feature: Endorser
| init | a | 100 | b | 200 | | init | a | 100 | b | 200 |
And user "binhn" sets VSCC to "my_vscc" for chaincode spec "cc_spec" And user "binhn" sets VSCC to "my_vscc" for chaincode spec "cc_spec"
And user "binhn" creates a deployment proposal "proposal1" using chaincode spec "cc_spec" And user "binhn" creates a deployment proposal "proposal1" using chaincode spec "cc_spec"
And user "binhn" sends proposal "proposal1" to endorsers with timeout of "2" seconds: And user "binhn" sends proposal "proposal1" to endorsers with timeout of "20" seconds:
| vp0 | vp1 | vp2 | vp3 | | vp0 | vp1 | vp2 | vp3 |
And user "binhn" stores their last result as "proposal1Responses" And user "binhn" stores their last result as "proposal1Responses"
Then user "binhn" expects proposal responses "proposal1Responses" with status "200" from endorsers: Then user "binhn" expects proposal responses "proposal1Responses" with status "200" from endorsers:
......
...@@ -45,9 +45,6 @@ import ( ...@@ -45,9 +45,6 @@ import (
// Logger for the shim package. // Logger for the shim package.
var chaincodeLogger = logging.MustGetLogger("shim") var chaincodeLogger = logging.MustGetLogger("shim")
// Handler to shim that handles all control logic.
var handler *Handler
// ChaincodeStub is an object passed to chaincode for shim side handling of // ChaincodeStub is an object passed to chaincode for shim side handling of
// APIs. // APIs.
type ChaincodeStub struct { type ChaincodeStub struct {
...@@ -55,6 +52,7 @@ type ChaincodeStub struct { ...@@ -55,6 +52,7 @@ type ChaincodeStub struct {
securityContext *pb.ChaincodeSecurityContext securityContext *pb.ChaincodeSecurityContext
chaincodeEvent *pb.ChaincodeEvent chaincodeEvent *pb.ChaincodeEvent
args [][]byte args [][]byte
handler *Handler
} }
// Peer address derived from command line or env var // Peer address derived from command line or env var
...@@ -150,7 +148,7 @@ func newPeerClientConnection() (*grpc.ClientConn, error) { ...@@ -150,7 +148,7 @@ func newPeerClientConnection() (*grpc.ClientConn, error) {
func chatWithPeer(chaincodename string, stream PeerChaincodeStream, cc Chaincode) error { func chatWithPeer(chaincodename string, stream PeerChaincodeStream, cc Chaincode) error {
// Create the shim handler responsible for all control logic // Create the shim handler responsible for all control logic
handler = newChaincodeHandler(stream, cc) handler := newChaincodeHandler(stream, cc)
defer stream.CloseSend() defer stream.CloseSend()
// Send the ChaincodeID during register. // Send the ChaincodeID during register.
...@@ -232,7 +230,7 @@ func chatWithPeer(chaincodename string, stream PeerChaincodeStream, cc Chaincode ...@@ -232,7 +230,7 @@ func chatWithPeer(chaincodename string, stream PeerChaincodeStream, cc Chaincode
// -- init stub --- // -- init stub ---
// ChaincodeInvocation functionality // ChaincodeInvocation functionality
func (stub *ChaincodeStub) init(uuid string, secContext *pb.ChaincodeSecurityContext) { func (stub *ChaincodeStub) init(handler *Handler, uuid string, secContext *pb.ChaincodeSecurityContext) {
stub.UUID = uuid stub.UUID = uuid
stub.securityContext = secContext stub.securityContext = secContext
stub.args = [][]byte{} stub.args = [][]byte{}
...@@ -243,6 +241,7 @@ func (stub *ChaincodeStub) init(uuid string, secContext *pb.ChaincodeSecurityCon ...@@ -243,6 +241,7 @@ func (stub *ChaincodeStub) init(uuid string, secContext *pb.ChaincodeSecurityCon
} else { } else {
panic("Arguments cannot be unmarshalled.") panic("Arguments cannot be unmarshalled.")
} }
stub.handler = handler
} }
func InitTestStub(funargs ...string) *ChaincodeStub { func InitTestStub(funargs ...string) *ChaincodeStub {
...@@ -250,7 +249,7 @@ func InitTestStub(funargs ...string) *ChaincodeStub { ...@@ -250,7 +249,7 @@ func InitTestStub(funargs ...string) *ChaincodeStub {
allargs := util.ToChaincodeArgs(funargs...) allargs := util.ToChaincodeArgs(funargs...)
newCI := pb.ChaincodeInput{Args: allargs} newCI := pb.ChaincodeInput{Args: allargs}
pl, _ := proto.Marshal(&newCI) pl, _ := proto.Marshal(&newCI)
stub.init("TEST-uuid", &pb.ChaincodeSecurityContext{Payload: pl}) stub.init(&Handler{}, "TEST-uuid", &pb.ChaincodeSecurityContext{Payload: pl})
return &stub return &stub
} }
...@@ -263,31 +262,31 @@ func InitTestStub(funargs ...string) *ChaincodeStub { ...@@ -263,31 +262,31 @@ func InitTestStub(funargs ...string) *ChaincodeStub {
// same transaction context; that is, chaincode calling chaincode doesn't // same transaction context; that is, chaincode calling chaincode doesn't
// create a new transaction message. // create a new transaction message.
func (stub *ChaincodeStub) InvokeChaincode(chaincodeName string, args [][]byte) ([]byte, error) { func (stub *ChaincodeStub) InvokeChaincode(chaincodeName string, args [][]byte) ([]byte, error) {
return handler.handleInvokeChaincode(chaincodeName, args, stub.UUID) return stub.handler.handleInvokeChaincode(chaincodeName, args, stub.UUID)
} }
// QueryChaincode locally calls the specified chaincode `Query` using the // QueryChaincode locally calls the specified chaincode `Query` using the
// same transaction context; that is, chaincode calling chaincode doesn't // same transaction context; that is, chaincode calling chaincode doesn't
// create a new transaction message. // create a new transaction message.
func (stub *ChaincodeStub) QueryChaincode(chaincodeName string, args [][]byte) ([]byte, error) { func (stub *ChaincodeStub) QueryChaincode(chaincodeName string, args [][]byte) ([]byte, error) {
return handler.handleQueryChaincode(chaincodeName, args, stub.UUID) return stub.handler.handleQueryChaincode(chaincodeName, args, stub.UUID)
} }
// --------- State functions ---------- // --------- State functions ----------
// GetState returns the byte array value specified by the `key`. // GetState returns the byte array value specified by the `key`.
func (stub *ChaincodeStub) GetState(key string) ([]byte, error) { func (stub *ChaincodeStub) GetState(key string) ([]byte, error) {
return handler.handleGetState(key, stub.UUID) return stub.handler.handleGetState(key, stub.UUID)
} }
// PutState writes the specified `value` and `key` into the ledger. // PutState writes the specified `value` and `key` into the ledger.
func (stub *ChaincodeStub) PutState(key string, value []byte) error { func (stub *ChaincodeStub) PutState(key string, value []byte) error {
return handler.handlePutState(key, value, stub.UUID) return stub.handler.handlePutState(key, value, stub.UUID)
} }
// DelState removes the specified `key` and its value from the ledger. // DelState removes the specified `key` and its value from the ledger.
func (stub *ChaincodeStub) DelState(key string) error { func (stub *ChaincodeStub) DelState(key string) error {
return handler.handleDelState(key, stub.UUID) return stub.handler.handleDelState(key, stub.UUID)
} }
//ReadCertAttribute is used to read an specific attribute from the transaction certificate, *attributeName* is passed as input parameter to this function. //ReadCertAttribute is used to read an specific attribute from the transaction certificate, *attributeName* is passed as input parameter to this function.
...@@ -338,11 +337,11 @@ type StateRangeQueryIterator struct { ...@@ -338,11 +337,11 @@ type StateRangeQueryIterator struct {
// between the startKey and endKey, inclusive. The order in which keys are // between the startKey and endKey, inclusive. The order in which keys are
// returned by the iterator is random. // returned by the iterator is random.
func (stub *ChaincodeStub) RangeQueryState(startKey, endKey string) (StateRangeQueryIteratorInterface, error) { func (stub *ChaincodeStub) RangeQueryState(startKey, endKey string) (StateRangeQueryIteratorInterface, error) {
response, err := handler.handleRangeQueryState(startKey, endKey, stub.UUID) response, err := stub.handler.handleRangeQueryState(startKey, endKey, stub.UUID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &StateRangeQueryIterator{handler, stub.UUID, response, 0}, nil return &StateRangeQueryIterator{stub.handler, stub.UUID, response, 0}, nil
} }
// HasNext returns true if the range query iterator contains additional keys // HasNext returns true if the range query iterator contains additional keys
......
...@@ -222,7 +222,7 @@ func (handler *Handler) handleInit(msg *pb.ChaincodeMessage) { ...@@ -222,7 +222,7 @@ func (handler *Handler) handleInit(msg *pb.ChaincodeMessage) {
// Call chaincode's Run // Call chaincode's Run
// Create the ChaincodeStub which the chaincode can use to callback // Create the ChaincodeStub which the chaincode can use to callback
stub := new(ChaincodeStub) stub := new(ChaincodeStub)
stub.init(msg.Txid, msg.SecurityContext) stub.init(handler, msg.Txid, msg.SecurityContext)
res, err := handler.cc.Init(stub) res, err := handler.cc.Init(stub)
// delete isTransaction entry // delete isTransaction entry
...@@ -289,7 +289,7 @@ func (handler *Handler) handleTransaction(msg *pb.ChaincodeMessage) { ...@@ -289,7 +289,7 @@ func (handler *Handler) handleTransaction(msg *pb.ChaincodeMessage) {
// Call chaincode's Run // Call chaincode's Run
// Create the ChaincodeStub which the chaincode can use to callback // Create the ChaincodeStub which the chaincode can use to callback
stub := new(ChaincodeStub) stub := new(ChaincodeStub)
stub.init(msg.Txid, msg.SecurityContext) stub.init(handler, msg.Txid, msg.SecurityContext)
res, err := handler.cc.Invoke(stub) res, err := handler.cc.Invoke(stub)
// delete isTransaction entry // delete isTransaction entry
...@@ -336,7 +336,7 @@ func (handler *Handler) handleQuery(msg *pb.ChaincodeMessage) { ...@@ -336,7 +336,7 @@ func (handler *Handler) handleQuery(msg *pb.ChaincodeMessage) {
// Call chaincode's Query // Call chaincode's Query
// Create the ChaincodeStub which the chaincode can use to callback // Create the ChaincodeStub which the chaincode can use to callback
stub := new(ChaincodeStub) stub := new(ChaincodeStub)
stub.init(msg.Txid, msg.SecurityContext) stub.init(handler, msg.Txid, msg.SecurityContext)
res, err := handler.cc.Query(stub) res, err := handler.cc.Query(stub)
// delete isTransaction entry // delete isTransaction entry
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"github.com/hyperledger/fabric/core/system_chaincode/api" "github.com/hyperledger/fabric/core/system_chaincode/api"
//import system chain codes here //import system chain codes here
"github.com/hyperledger/fabric/bddtests/syschaincode/noop" "github.com/hyperledger/fabric/bddtests/syschaincode/noop"
"github.com/hyperledger/fabric/core/system_chaincode/lccc"
) )
//see systemchaincode_test.go for an example using "sample_syscc" //see systemchaincode_test.go for an example using "sample_syscc"
...@@ -30,6 +31,13 @@ var systemChaincodes = []*api.SystemChaincode{ ...@@ -30,6 +31,13 @@ var systemChaincodes = []*api.SystemChaincode{
Path: "github.com/hyperledger/fabric/bddtests/syschaincode/noop", Path: "github.com/hyperledger/fabric/bddtests/syschaincode/noop",
InitArgs: [][]byte{}, InitArgs: [][]byte{},
Chaincode: &noop.SystemChaincode{}, Chaincode: &noop.SystemChaincode{},
},
{
Enabled: true,
Name: "lccc",
Path: "github.com/hyperledger/fabric/core/system_chaincode/lccc",
InitArgs: [][]byte{[]byte("")},
Chaincode: &lccc.LifeCycleSysCC{},
}} }}
//RegisterSysCCs is the hook for system chaincodes where system chaincodes are registered with the fabric //RegisterSysCCs is the hook for system chaincodes where system chaincodes are registered with the fabric
......
/*
Copyright IBM Corp. 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 lccc
import (
"fmt"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/core/chaincode"
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos"
"github.com/op/go-logging"
"golang.org/x/net/context"
)
//The life cycle system chaincode manages chaincodes deployed
//on this peer. It manages chaincodes via Invoke proposals.
// "Args":["deploy",<ChaincodeDeploymentSpec>]
// "Args":["upgrade",<ChaincodeDeploymentSpec>]
// "Args":["stop",<ChaincodeInvocationSpec>]
// "Args":["start",<ChaincodeInvocationSpec>]
var logger = logging.MustGetLogger("lccc")
const (
//CHAINCODETABLE prefix for chaincode tables
CHAINCODETABLE = "chaincodes"
//chaincode lifecyle commands
//DEPLOY deploy command
DEPLOY = "deploy"
//chaincode query commands
//GETCCINFO get chaincode
GETCCINFO = "getid"
)
//---------- the LCCC -----------------
// LifeCycleSysCC implements chaincode lifecycle and policies aroud it
type LifeCycleSysCC struct {
}
//----------------errors---------------
//AlreadyRegisteredErr Already registered error
type AlreadyRegisteredErr string
func (f AlreadyRegisteredErr) Error() string {
return fmt.Sprintf("%s already registered", string(f))
}
//InvalidFunctionErr invalid function error
type InvalidFunctionErr string
func (f InvalidFunctionErr) Error() string {
return fmt.Sprintf("invalid function to lccc %s", string(f))
}
//InvalidArgsLenErr invalid arguments length error
type InvalidArgsLenErr int
func (i InvalidArgsLenErr) Error() string {
return fmt.Sprintf("invalid number of argument to lccc %d", int(i))
}
//InvalidArgsErr invalid arguments error
type InvalidArgsErr int
func (i InvalidArgsErr) Error() string {
return fmt.Sprintf("invalid argument (%d) to lccc", int(i))
}
//TXExistsErr transaction exists error
type TXExistsErr string
func (t TXExistsErr) Error() string {
return fmt.Sprintf("transaction exists %s", string(t))
}
//TXNotFoundErr transaction not found error
type TXNotFoundErr string
func (t TXNotFoundErr) Error() string {
return fmt.Sprintf("transaction not found %s", string(t))
}
//InvalidDeploymentSpecErr invalide chaincode deployment spec error
type InvalidDeploymentSpecErr string
func (f InvalidDeploymentSpecErr) Error() string {
return fmt.Sprintf("Invalid deployment spec : %s", string(f))
}
//ChaincodeExistsErr chaincode exists error
type ChaincodeExistsErr string
func (t ChaincodeExistsErr) Error() string {
return fmt.Sprintf("Chaincode exists %s", string(t))
}
//InvalidChainNameErr invalid function error
type InvalidChainNameErr string
func (f InvalidChainNameErr) Error() string {
return fmt.Sprintf("invalid chain name %s", string(f))
}
//-------------- helper functions ------------------
//create the table to maintain list of chaincodes maintained in this
//blockchain.
func (lccc *LifeCycleSysCC) createChaincodeTable(stub shim.ChaincodeStubInterface, cctable string) error {
// Create table one
var colDefs []*shim.ColumnDefinition
nameColDef := shim.ColumnDefinition{Name: "name",
Type: shim.ColumnDefinition_STRING, Key: true}
versColDef := shim.ColumnDefinition{Name: "version",
Type: shim.ColumnDefinition_INT32, Key: false}
//QUESTION - Should code be separately maintained ?
codeDef := shim.ColumnDefinition{Name: "code",
Type: shim.ColumnDefinition_BYTES, Key: false}
colDefs = append(colDefs, &nameColDef)
colDefs = append(colDefs, &versColDef)
colDefs = append(colDefs, &codeDef)
return stub.CreateTable(cctable, colDefs)
}
//register create the chaincode table. name can be used to different
//tables of chaincodes. This would provide the way to associate chaincodes
//with chains(and ledgers)
func (lccc *LifeCycleSysCC) register(stub shim.ChaincodeStubInterface, name string) error {
ccname := CHAINCODETABLE + "-" + name
row, err := stub.GetTable(ccname)
if err == nil && row != nil { //table exists, do nothing
return AlreadyRegisteredErr(name)
}
//there may be other err's but assume "not exists". Anything
//more serious than that bound to show up
err = lccc.createChaincodeTable(stub, ccname)
return err
}
//create the chaincode on the given chain
func (lccc *LifeCycleSysCC) createChaincode(stub shim.ChaincodeStubInterface, chainname string, ccname string, cccode []byte) (*shim.Row, error) {
var columns []*shim.Column
nameCol := shim.Column{Value: &shim.Column_String_{String_: ccname}}
versCol := shim.Column{Value: &shim.Column_Int32{Int32: 0}}
codeCol := shim.Column{Value: &shim.Column_Bytes{Bytes: cccode}}
columns = append(columns, &nameCol)
columns = append(columns, &versCol)
columns = append(columns, &codeCol)
row := &shim.Row{Columns: columns}
_, err := stub.InsertRow(CHAINCODETABLE+"-"+chainname, *row)
if err != nil {
return nil, fmt.Errorf("insertion of chaincode failed. %s", err)
}
return row, nil
}
//checks for existence of chaincode on the given chain
func (lccc *LifeCycleSysCC) getChaincode(stub shim.ChaincodeStubInterface, chainname string, ccname string) (shim.Row, bool, error) {
var columns []shim.Column
nameCol := shim.Column{Value: &shim.Column_String_{String_: ccname}}
columns = append(columns, nameCol)
row, err := stub.GetRow(CHAINCODETABLE+"-"+chainname, columns)
if err != nil {
return shim.Row{}, false, err
}
if len(row.Columns) > 0 {
return row, true, nil
}
return row, false, nil
}
//getChaincodeDeploymentSpec returns a ChaincodeDeploymentSpec given args
func (lccc *LifeCycleSysCC) getChaincodeDeploymentSpec(code []byte) (*pb.ChaincodeDeploymentSpec, error) {
cds := &pb.ChaincodeDeploymentSpec{}
err := proto.Unmarshal(code, cds)
if err != nil {
return nil, InvalidDeploymentSpecErr(err.Error())
}
return cds, nil
}
//do access control
func (lccc *LifeCycleSysCC) acl(stub shim.ChaincodeStubInterface, chainname chaincode.ChainName, cds *pb.ChaincodeDeploymentSpec) error {
return nil
}
//check validity of chain name
func (lccc *LifeCycleSysCC) isValidChainName(chainname string) bool {
//TODO we probably need more checks and have
if chainname == "" {
return false
}
return true
}
//deploy the chaincode on to the chain
func (lccc *LifeCycleSysCC) deploy(stub shim.ChaincodeStubInterface, chainname string, cds *pb.ChaincodeDeploymentSpec) error {
_, exists, err := lccc.getChaincode(stub, chainname, cds.ChaincodeSpec.ChaincodeID.Name)
if exists {
return ChaincodeExistsErr(cds.ChaincodeSpec.ChaincodeID.Name)
}
//TODO : this needs to be converted to another data structure to be handled
// by the chaincode framework (which currently handles "Transaction")
t, err := lccc.toTransaction(cds)
if err != nil {
return fmt.Errorf("could not convert proposal to transaction %s", err)
}
//if unit testing, just return..we cannot do the actual deploy
if _, ismock := stub.(*shim.MockStub); ismock {
//we got this far just stop short of actual deploy for test purposes
return nil
}
ctxt := context.Background()
//TODO - create chaincode support for chainname, for now use DefaultChain
//chaincodeSupport := chaincode.GetChain(chaincode.ChainName(chainname))
chaincodeSupport := chaincode.GetChain(chaincode.DefaultChain)
_, err = chaincodeSupport.Deploy(ctxt, t)
if err != nil {
return fmt.Errorf("Failed to deploy chaincode spec(%s)", err)
}
//launch and wait for ready
_, _, err = chaincodeSupport.Launch(ctxt, t)
if err != nil {
return fmt.Errorf("%s", err)
}
/************ STOP WHEN WE ARE ON NEWLEDGER
//stop now that we are done
chaincodeSupport.Stop(ctxt, cds)
**********/
return nil
}
//this implements "deploy" Invoke transaction
func (lccc *LifeCycleSysCC) executeDeploy(stub shim.ChaincodeStubInterface, chainname string, code []byte) error {
//lazy creation of chaincode table for chainname...its possible
//there are chains without chaincodes
if err := lccc.register(stub, chainname); err != nil {
//if its already registered, ok... proceed
if _, ok := err.(AlreadyRegisteredErr); !ok {
return err
}
}
cds, err := lccc.getChaincodeDeploymentSpec(code)
if err != nil {
return err
}
if err = lccc.acl(stub, chaincode.DefaultChain, cds); err != nil {
return err
}
if err = lccc.deploy(stub, chainname, cds); err != nil {
return err
}
_, err = lccc.createChaincode(stub, chainname, cds.ChaincodeSpec.ChaincodeID.Name, cds.CodePackage)
return err
}
//TODO - this is temporary till we use Transaction in chaincode code
func (lccc *LifeCycleSysCC) toTransaction(cds *pb.ChaincodeDeploymentSpec) (*pb.Transaction, error) {
return pb.NewChaincodeDeployTransaction(cds, cds.ChaincodeSpec.ChaincodeID.Name)
}
//-------------- the chaincode stub interface implementation ----------
//Init does nothing
func (lccc *LifeCycleSysCC) Init(stub shim.ChaincodeStubInterface) ([]byte, error) {
return nil, nil
}
// Invoke implements lifecycle functions "deploy", "start", "stop", "upgrade".
// Deploy's arguments - {[]byte("deploy"), []byte(<chainname>), <unmarshalled pb.ChaincodeDeploymentSpec>}
//
// Invoke also implements some query-like functions
// Get chaincode arguments - {[]byte("getid"), []byte(<chainname>), []byte(<chaincodename>)}
func (lccc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) ([]byte, error) {
args := stub.GetArgs()
if len(args) < 1 {
return nil, InvalidArgsLenErr(len(args))
}
function := string(args[0])
switch function {
case DEPLOY:
if len(args) != 3 {
return nil, InvalidArgsLenErr(len(args))