Skip to content

Commit 09fe0da

Browse files
committed
[FAB-1238] implement upgrade of lccc
Add upgrade method for lccc. This method checkes DeploymentSpec , replaces existing DeploymentSpec with new one, and updates chaincode version with +1 simply. Finally return new version of chaincode. https://jira.hyperledger.org/browse/FAB-1238 Change-Id: I5b314915c0ad966896669bfe6c95bdbe69e82aeb Signed-off-by: jiangyaoguo <[email protected]>
1 parent 591d16d commit 09fe0da

File tree

4 files changed

+214
-10
lines changed

4 files changed

+214
-10
lines changed

core/chaincode/lccc.go

+69-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import (
3333
//define the datastructure for chaincodes to be serialized by proto
3434
type chaincodeData struct {
3535
name string `protobuf:"bytes,1,opt,name=name"`
36-
version int32 `protobuf:"bytes,2,opt,name=version,proto3"`
36+
version int32 `protobuf:"varint,2,opt,name=version,proto3"`
3737
depSpec []byte `protobuf:"bytes,3,opt,name=depSpec,proto3"`
3838
escc string `protobuf:"bytes,4,opt,name=escc"`
3939
vscc string `protobuf:"bytes,5,opt,name=vscc"`
@@ -63,6 +63,9 @@ const (
6363
//DEPLOY deploy command
6464
DEPLOY = "deploy"
6565

66+
//UPGRADE upgrade chaincode
67+
UPGRADE = "upgrade"
68+
6669
//GETCCINFO get chaincode
6770
GETCCINFO = "getid"
6871

@@ -71,6 +74,9 @@ const (
7174

7275
//characters used in chaincodenamespace
7376
specialChars = "/:[]${}"
77+
78+
// chaincode version when deploy
79+
startVersion = 0
7480
)
7581

7682
//---------- the LCCC -----------------
@@ -137,6 +143,12 @@ func (t ExistsErr) Error() string {
137143
return fmt.Sprintf("Chaincode exists %s", string(t))
138144
}
139145

146+
type ChaincodeNotFoundErr string
147+
148+
func (t ChaincodeNotFoundErr) Error() string {
149+
return fmt.Sprintf("chaincode not found %s", string(t))
150+
}
151+
140152
//InvalidChainNameErr invalid chain name error
141153
type InvalidChainNameErr string
142154

@@ -161,7 +173,17 @@ func (m MarshallErr) Error() string {
161173
//-------------- helper functions ------------------
162174
//create the chaincode on the given chain
163175
func (lccc *LifeCycleSysCC) createChaincode(stub shim.ChaincodeStubInterface, chainname string, ccname string, cccode []byte) (*chaincodeData, error) {
164-
cd := &chaincodeData{name: ccname, depSpec: cccode}
176+
return lccc.putChaincodeData(stub, chainname, ccname, startVersion, cccode)
177+
}
178+
179+
//upgrade the chaincode on the given chain
180+
func (lccc *LifeCycleSysCC) upgradeChaincode(stub shim.ChaincodeStubInterface, chainname string, ccname string, version int32, cccode []byte) (*chaincodeData, error) {
181+
return lccc.putChaincodeData(stub, chainname, ccname, version, cccode)
182+
}
183+
184+
//create the chaincode on the given chain
185+
func (lccc *LifeCycleSysCC) putChaincodeData(stub shim.ChaincodeStubInterface, chainname string, ccname string, version int32, cccode []byte) (*chaincodeData, error) {
186+
cd := &chaincodeData{name: ccname, version: version, depSpec: cccode}
165187
cdbytes, err := proto.Marshal(cd)
166188
if err != nil {
167189
return nil, err
@@ -319,6 +341,39 @@ func (lccc *LifeCycleSysCC) executeDeploy(stub shim.ChaincodeStubInterface, chai
319341
return err
320342
}
321343

344+
//this implements "upgrade" Invoke transaction
345+
func (lccc *LifeCycleSysCC) executeUpgrade(stub shim.ChaincodeStubInterface, chainName string, code []byte) ([]byte, error) {
346+
cds, err := lccc.getChaincodeDeploymentSpec(code)
347+
if err != nil {
348+
return nil, err
349+
}
350+
351+
chaincodeName := cds.ChaincodeSpec.ChaincodeID.Name
352+
if !lccc.isValidChaincodeName(chaincodeName) {
353+
return nil, InvalidChaincodeNameErr(chaincodeName)
354+
}
355+
356+
// check for existence of chaincode
357+
cd, err := lccc.getChaincode(stub, chainName, chaincodeName)
358+
if cd == nil {
359+
return nil, ChaincodeNotFoundErr(chainName)
360+
}
361+
362+
if err = lccc.acl(stub, chainName, cds); err != nil {
363+
return nil, err
364+
}
365+
366+
// replace the ChaincodeDeploymentSpec
367+
newVersion := cd.version + 1
368+
newCD, err := lccc.upgradeChaincode(stub, chainName, chaincodeName, newVersion, code)
369+
if err != nil {
370+
return nil, err
371+
}
372+
373+
strVer := fmt.Sprint(newCD.version)
374+
return []byte(strVer), nil
375+
}
376+
322377
//-------------- the chaincode stub interface implementation ----------
323378

324379
//Init does nothing
@@ -359,6 +414,18 @@ func (lccc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) ([]byte, er
359414
err := lccc.executeDeploy(stub, chainname, code)
360415

361416
return nil, err
417+
case UPGRADE:
418+
if len(args) != 3 {
419+
return nil, InvalidArgsLenErr(len(args))
420+
}
421+
422+
chainname := string(args[1])
423+
if !lccc.isValidChainName(chainname) {
424+
return nil, InvalidChainNameErr(chainname)
425+
}
426+
427+
code := args[2]
428+
return lccc.executeUpgrade(stub, chainname, code)
362429
case GETCCINFO, GETDEPSPEC:
363430
if len(args) != 3 {
364431
return nil, InvalidArgsLenErr(len(args))

core/chaincode/lccc_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,71 @@ func TestRetryFailedDeploy(t *testing.T) {
248248
t.FailNow()
249249
}
250250
}
251+
252+
//TestUpgrade tests the upgrade function
253+
func TestUpgrade(t *testing.T) {
254+
initialize()
255+
256+
scc := new(LifeCycleSysCC)
257+
stub := shim.NewMockStub("lccc", scc)
258+
259+
cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
260+
var b []byte
261+
if b, err = proto.Marshal(cds); err != nil || b == nil {
262+
t.Fatalf("Marshal DeploymentSpec failed")
263+
}
264+
265+
args := [][]byte{[]byte(DEPLOY), []byte("test"), b}
266+
if _, err := stub.MockInvoke("1", args); err != nil {
267+
t.Fatalf("Deploy chaincode error: %v", err)
268+
}
269+
270+
newCds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
271+
var newb []byte
272+
if newb, err = proto.Marshal(newCds); err != nil || newb == nil {
273+
t.Fatalf("Marshal DeploymentSpec failed")
274+
}
275+
276+
args = [][]byte{[]byte(UPGRADE), []byte("test"), newb}
277+
version, err := stub.MockInvoke("1", args)
278+
if err != nil {
279+
t.Fatalf("Upgrade chaincode error: %v", err)
280+
}
281+
282+
expectVer := "1"
283+
newVer := string(version)
284+
if newVer != expectVer {
285+
t.Fatalf("Upgrade chaincode version error, expected %s, got %s", expectVer, newVer)
286+
}
287+
}
288+
289+
//TestUpgradeNonExistChaincode tests upgrade non exist chaincode
290+
func TestUpgradeNonExistChaincode(t *testing.T) {
291+
initialize()
292+
293+
scc := new(LifeCycleSysCC)
294+
stub := shim.NewMockStub("lccc", scc)
295+
296+
cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
297+
var b []byte
298+
if b, err = proto.Marshal(cds); err != nil || b == nil {
299+
t.Fatalf("Marshal DeploymentSpec failed")
300+
}
301+
302+
args := [][]byte{[]byte(DEPLOY), []byte("test"), b}
303+
if _, err := stub.MockInvoke("1", args); err != nil {
304+
t.Fatalf("Deploy chaincode error: %v", err)
305+
}
306+
307+
newCds, err := constructDeploymentSpec("example03", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
308+
var newb []byte
309+
if newb, err = proto.Marshal(newCds); err != nil || newb == nil {
310+
t.Fatalf("Marshal DeploymentSpec failed")
311+
}
312+
313+
args = [][]byte{[]byte(UPGRADE), []byte("test"), newb}
314+
_, err = stub.MockInvoke("1", args)
315+
if _, ok := err.(ChaincodeNotFoundErr); !ok {
316+
t.FailNow()
317+
}
318+
}

core/endorser/endorser.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func (e *Endorser) callChaincode(ctxt context.Context, chainID string, txid stri
105105
//
106106
//NOTE that if there's an error all simulation, including the chaincode
107107
//table changes in lccc will be thrown away
108-
if cid.Name == "lccc" && len(cis.ChaincodeSpec.CtorMsg.Args) == 3 && string(cis.ChaincodeSpec.CtorMsg.Args[0]) == "deploy" {
108+
if cid.Name == "lccc" && len(cis.ChaincodeSpec.CtorMsg.Args) == 3 && (string(cis.ChaincodeSpec.CtorMsg.Args[0]) == "deploy" || string(cis.ChaincodeSpec.CtorMsg.Args[0]) == "upgrade") {
109109
var cds *pb.ChaincodeDeploymentSpec
110110
cds, err = putils.GetChaincodeDeploymentSpec(cis.ChaincodeSpec.CtorMsg.Args[2])
111111
if err != nil {

core/endorser/endorser_test.go

+76-7
Original file line numberDiff line numberDiff line change
@@ -119,24 +119,38 @@ func closeListenerAndSleep(l net.Listener) {
119119

120120
//getProposal gets the proposal for the chaincode invocation
121121
//Currently supported only for Invokes (Queries still go through devops client)
122-
func getProposal(cis *pb.ChaincodeInvocationSpec, chainID string, creator []byte) (*pb.Proposal, error) {
122+
func getInvokeProposal(cis *pb.ChaincodeInvocationSpec, chainID string, creator []byte) (*pb.Proposal, error) {
123123
uuid := util.GenerateUUID()
124124
return pbutils.CreateChaincodeProposal(uuid, chainID, cis, creator)
125125
}
126126

127-
//getDeployProposal gets the proposal for the chaincode deployment
128-
//the payload is a ChaincodeDeploymentSpec
129127
func getDeployProposal(cds *pb.ChaincodeDeploymentSpec, chainID string, creator []byte) (*pb.Proposal, error) {
128+
return getDeployOrUpgradeProposal(cds, chainID, creator, false)
129+
}
130+
131+
func getUpgradeProposal(cds *pb.ChaincodeDeploymentSpec, chainID string, creator []byte) (*pb.Proposal, error) {
132+
return getDeployOrUpgradeProposal(cds, chainID, creator, true)
133+
}
134+
135+
//getDeployOrUpgradeProposal gets the proposal for the chaincode deploy or upgrade
136+
//the payload is a ChaincodeDeploymentSpec
137+
func getDeployOrUpgradeProposal(cds *pb.ChaincodeDeploymentSpec, chainID string, creator []byte, upgrade bool) (*pb.Proposal, error) {
130138
b, err := proto.Marshal(cds)
131139
if err != nil {
132140
return nil, err
133141
}
134142

143+
var propType string
144+
if upgrade {
145+
propType = "upgrade"
146+
} else {
147+
propType = "deploy"
148+
}
135149
//wrap the deployment in an invocation spec to lccc...
136-
lcccSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeID: &pb.ChaincodeID{Name: "lccc"}, CtorMsg: &pb.ChaincodeInput{Args: [][]byte{[]byte("deploy"), []byte(chainID), b}}}}
150+
lcccSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeID: &pb.ChaincodeID{Name: "lccc"}, CtorMsg: &pb.ChaincodeInput{Args: [][]byte{[]byte(propType), []byte(chainID), b}}}}
137151

138152
//...and get the proposal for it
139-
return getProposal(lcccSpec, chainID, creator)
153+
return getInvokeProposal(lcccSpec, chainID, creator)
140154
}
141155

142156
func getSignedProposal(prop *pb.Proposal, signer msp.SigningIdentity) (*pb.SignedProposal, error) {
@@ -163,6 +177,14 @@ func getDeploymentSpec(context context.Context, spec *pb.ChaincodeSpec) (*pb.Cha
163177
}
164178

165179
func deploy(endorserServer pb.EndorserServer, chainID string, spec *pb.ChaincodeSpec, f func(*pb.ChaincodeDeploymentSpec)) (*pb.ProposalResponse, *pb.Proposal, error) {
180+
return deployOrUpgrade(endorserServer, chainID, spec, f, false)
181+
}
182+
183+
func upgrade(endorserServer pb.EndorserServer, chainID string, spec *pb.ChaincodeSpec, f func(*pb.ChaincodeDeploymentSpec)) (*pb.ProposalResponse, *pb.Proposal, error) {
184+
return deployOrUpgrade(endorserServer, chainID, spec, f, true)
185+
}
186+
187+
func deployOrUpgrade(endorserServer pb.EndorserServer, chainID string, spec *pb.ChaincodeSpec, f func(*pb.ChaincodeDeploymentSpec), upgrade bool) (*pb.ProposalResponse, *pb.Proposal, error) {
166188
var err error
167189
var depSpec *pb.ChaincodeDeploymentSpec
168190

@@ -182,7 +204,11 @@ func deploy(endorserServer pb.EndorserServer, chainID string, spec *pb.Chaincode
182204
}
183205

184206
var prop *pb.Proposal
185-
prop, err = getDeployProposal(depSpec, chainID, creator)
207+
if upgrade {
208+
prop, err = getUpgradeProposal(depSpec, chainID, creator)
209+
} else {
210+
prop, err = getDeployProposal(depSpec, chainID, creator)
211+
}
186212
if err != nil {
187213
return nil, nil, err
188214
}
@@ -208,7 +234,7 @@ func invoke(chainID string, spec *pb.ChaincodeSpec) (*pb.ProposalResponse, error
208234
}
209235

210236
var prop *pb.Proposal
211-
prop, err = getProposal(invocation, chainID, creator)
237+
prop, err = getInvokeProposal(invocation, chainID, creator)
212238
if err != nil {
213239
return nil, fmt.Errorf("Error creating proposal %s: %s\n", spec.ChaincodeID, err)
214240
}
@@ -352,6 +378,49 @@ func TestDeployAndInvoke(t *testing.T) {
352378
chaincode.GetChain().Stop(ctxt, chainID, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeID: chaincodeID}})
353379
}
354380

381+
// TestUpgradeAndInvoke deploys chaincode_example01, upgrade it with chaincode_example02, then invoke it
382+
func TestDeployAndUpgrade(t *testing.T) {
383+
chainID := util.GetTestChainID()
384+
var ctxt = context.Background()
385+
386+
url1 := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example01"
387+
url2 := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02"
388+
chaincodeID1 := &pb.ChaincodeID{Path: url1, Name: "upgradeex01"}
389+
chaincodeID2 := &pb.ChaincodeID{Path: url2, Name: "upgradeex01"}
390+
391+
f := "init"
392+
argsDeploy := util.ToChaincodeArgs(f, "a", "100", "b", "200")
393+
spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: chaincodeID1, CtorMsg: &pb.ChaincodeInput{Args: argsDeploy}}
394+
resp, prop, err := deploy(endorserServer, chainID, spec, nil)
395+
chaincodeName := spec.ChaincodeID.Name
396+
if err != nil {
397+
t.Fail()
398+
t.Logf("Error deploying <%s>: %s", chaincodeName, err)
399+
return
400+
}
401+
402+
err = endorserServer.(*Endorser).commitTxSimulation(prop, chainID, signer, resp)
403+
if err != nil {
404+
t.Fail()
405+
t.Logf("Error committing <%s>: %s", chaincodeName, err)
406+
return
407+
}
408+
409+
argsUpgrade := util.ToChaincodeArgs(f, "a", "150", "b", "300")
410+
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: chaincodeID2, CtorMsg: &pb.ChaincodeInput{Args: argsUpgrade}}
411+
resp, prop, err = upgrade(endorserServer, chainID, spec, nil)
412+
if err != nil {
413+
t.Fail()
414+
t.Logf("Error upgrading <%s>: %s", chaincodeName, err)
415+
return
416+
}
417+
418+
fmt.Printf("Upgrade test passed\n")
419+
t.Logf("Upgrade test passed")
420+
421+
chaincode.GetChain().Stop(ctxt, chainID, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeID: chaincodeID2}})
422+
}
423+
355424
func TestMain(m *testing.M) {
356425
SetupTestConfig()
357426
viper.Set("peer.fileSystemPath", filepath.Join(os.TempDir(), "hyperledger", "production"))

0 commit comments

Comments
 (0)