Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Commit 20fb840

Browse files
[FAB-3783] go-sdk chaincode upgrade support
Change-Id: I0455c4dfed54d0f9f4f1e4d3ca4e32f2dd8911ee Signed-off-by: bryan-huangyan <bryan.huang.yan@gmail.com>
1 parent 9cde6ca commit 20fb840

File tree

6 files changed

+222
-0
lines changed

6 files changed

+222
-0
lines changed

api/apifabclient/channel.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type Channel interface {
3232
IsInitialized() bool
3333
LoadConfigUpdateEnvelope(data []byte) error
3434
SendInstantiateProposal(chaincodeName string, args []string, chaincodePath string, chaincodeVersion string, chaincodePolicy *common.SignaturePolicyEnvelope, targets []txn.ProposalProcessor) ([]*txn.TransactionProposalResponse, txn.TransactionID, error)
35+
SendUpgradeProposal(chaincodeName string, args []string, chaincodePath string, chaincodeVersion string, chaincodePolicy *common.SignaturePolicyEnvelope, targets []txn.ProposalProcessor) ([]*txn.TransactionProposalResponse, txn.TransactionID, error)
3536

3637
// Network
3738
// TODO: Use PeerEndorser

pkg/fabric-client/channel/txnsender.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,75 @@ func (c *Channel) SendInstantiateProposal(chaincodeName string,
223223
return transactionProposalResponse, txnID, err
224224
}
225225

226+
// SendUpgradeProposal sends an upgrade proposal to one or more endorsing peers.
227+
// chaincodeName: required - The name of the chain.
228+
// args: optional - string Array arguments specific to the chaincode being upgraded
229+
// chaincodePath: required - string of the path to the location of the source code of the chaincode
230+
// chaincodeVersion: required - string of the version of the chaincode
231+
func (c *Channel) SendUpgradeProposal(chaincodeName string,
232+
args []string, chaincodePath string, chaincodeVersion string,
233+
chaincodePolicy *common.SignaturePolicyEnvelope, targets []apitxn.ProposalProcessor) ([]*apitxn.TransactionProposalResponse, apitxn.TransactionID, error) {
234+
235+
if chaincodeName == "" {
236+
return nil, apitxn.TransactionID{}, fmt.Errorf("Missing 'chaincodeName' parameter")
237+
}
238+
if chaincodePath == "" {
239+
return nil, apitxn.TransactionID{}, fmt.Errorf("Missing 'chaincodePath' parameter")
240+
}
241+
if chaincodeVersion == "" {
242+
return nil, apitxn.TransactionID{}, fmt.Errorf("Missing 'chaincodeVersion' parameter")
243+
}
244+
if chaincodePolicy == nil {
245+
return nil, apitxn.TransactionID{}, fmt.Errorf("Missing 'chaincodePolicy' parameter")
246+
}
247+
248+
// TODO: We should validate that targets are added to the channel.
249+
if targets == nil || len(targets) < 1 {
250+
return nil, apitxn.TransactionID{}, fmt.Errorf("Missing peer objects for upgrade CC proposal")
251+
}
252+
253+
argsArray := make([][]byte, len(args))
254+
for i, arg := range args {
255+
argsArray[i] = []byte(arg)
256+
}
257+
258+
ccds := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{
259+
Type: pb.ChaincodeSpec_GOLANG, ChaincodeId: &pb.ChaincodeID{Name: chaincodeName, Path: chaincodePath, Version: chaincodeVersion},
260+
Input: &pb.ChaincodeInput{Args: argsArray}}}
261+
262+
if c.clientContext.UserContext() == nil {
263+
return nil, apitxn.TransactionID{}, fmt.Errorf("User context needs to be set")
264+
}
265+
creator, err := c.clientContext.UserContext().Identity()
266+
if err != nil {
267+
return nil, apitxn.TransactionID{}, fmt.Errorf("Error getting creator: %v", err)
268+
}
269+
chaincodePolicyBytes, err := protos_utils.Marshal(chaincodePolicy)
270+
if err != nil {
271+
return nil, apitxn.TransactionID{}, err
272+
}
273+
// create a proposal from a chaincodeDeploymentSpec
274+
proposal, txID, err := protos_utils.CreateUpgradeProposalFromCDS(c.Name(), ccds, creator, chaincodePolicyBytes, []byte("escc"), []byte("vscc"))
275+
if err != nil {
276+
return nil, apitxn.TransactionID{}, fmt.Errorf("Could not create chaincode Upgrade proposal, err %s", err)
277+
}
278+
279+
signedProposal, err := c.signProposal(proposal)
280+
if err != nil {
281+
return nil, apitxn.TransactionID{}, err
282+
}
283+
284+
txnID := apitxn.TransactionID{ID: txID} // Nonce is missing
285+
286+
transactionProposalResponse, err := txnproc.SendTransactionProposalToProcessors(&apitxn.TransactionProposal{
287+
SignedProposal: signedProposal,
288+
Proposal: proposal,
289+
TxnID: txnID,
290+
}, targets)
291+
292+
return transactionProposalResponse, txnID, err
293+
}
294+
226295
// SignPayload ... TODO.
227296
func (c *Channel) SignPayload(payload []byte) (*fab.SignedEnvelope, error) {
228297
//Get user info

pkg/fabric-client/channel/txnsender_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ func TestCreateTransaction(t *testing.T) {
125125
//TODO: Need actual sample payload for success case
126126

127127
}
128+
128129
func TestSendInstantiateProposal(t *testing.T) {
129130
//Setup channel
130131
client := mocks.NewMockClient()
@@ -193,6 +194,74 @@ func TestSendInstantiateProposal(t *testing.T) {
193194
}
194195
}
195196

197+
func TestSendUpgradeProposal(t *testing.T) {
198+
//Setup channel
199+
client := mocks.NewMockClient()
200+
user := mocks.NewMockUserWithMSPID("test", "1234")
201+
cryptoSuite := &mocks.MockCryptoSuite{}
202+
client.SaveUserToStateStore(user, true)
203+
client.SetCryptoSuite(cryptoSuite)
204+
client.SetUserContext(user)
205+
channel, _ := NewChannel("testChannel", client)
206+
207+
mockCtrl := gomock.NewController(t)
208+
defer mockCtrl.Finish()
209+
proc := mock_apitxn.NewMockProposalProcessor(mockCtrl)
210+
211+
tp := apitxn.TransactionProposal{SignedProposal: &pb.SignedProposal{}}
212+
tpr := apitxn.TransactionProposalResult{Endorser: "example.com", Status: 99, Proposal: tp, ProposalResponse: nil}
213+
214+
proc.EXPECT().ProcessTransactionProposal(gomock.Any()).Return(tpr, nil)
215+
targets := []apitxn.ProposalProcessor{proc}
216+
217+
//Add a Peer
218+
peer := mocks.MockPeer{MockName: "Peer1", MockURL: "http://peer1.com", MockRoles: []string{}, MockCert: nil}
219+
channel.AddPeer(&peer)
220+
221+
tresponse, txnid, err := channel.SendUpgradeProposal("", nil, "",
222+
"", cauthdsl.SignedByMspMember("Org1MSP"), targets)
223+
224+
if err == nil || err.Error() != "Missing 'chaincodeName' parameter" {
225+
t.Fatal("Validation for chain code name parameter for send Upgrade Proposal failed")
226+
}
227+
228+
tresponse, txnid, err = channel.SendUpgradeProposal("qscc", nil, "",
229+
"", cauthdsl.SignedByMspMember("Org1MSP"), targets)
230+
231+
tresponse, txnid, err = channel.SendUpgradeProposal("qscc", nil, "",
232+
"", cauthdsl.SignedByMspMember("Org1MSP"), targets)
233+
234+
if err == nil || err.Error() != "Missing 'chaincodePath' parameter" {
235+
t.Fatal("Validation for chain code path for send Upgrade Proposal failed")
236+
}
237+
238+
tresponse, txnid, err = channel.SendUpgradeProposal("qscc", nil, "test",
239+
"", cauthdsl.SignedByMspMember("Org1MSP"), targets)
240+
241+
if err == nil || err.Error() != "Missing 'chaincodeVersion' parameter" {
242+
t.Fatal("Validation for chain code version for send Upgrade Proposal failed")
243+
}
244+
245+
tresponse, txnid, err = channel.SendUpgradeProposal("qscc", nil, "test",
246+
"2", nil, nil)
247+
if err == nil || err.Error() != "Missing 'chaincodePolicy' parameter" {
248+
t.Fatal("Validation for chain code policy for send Upgrade Proposal failed")
249+
}
250+
251+
tresponse, txnid, err = channel.SendUpgradeProposal("qscc", nil, "test",
252+
"2", cauthdsl.SignedByMspMember("Org1MSP"), targets)
253+
254+
if err != nil || len(tresponse) == 0 || txnid.ID == "" {
255+
t.Fatal("Send Upgrade Proposal Test failed")
256+
}
257+
258+
tresponse, txnid, err = channel.SendUpgradeProposal("qscc", nil, "test",
259+
"2", cauthdsl.SignedByMspMember("Org1MSP"), nil)
260+
if err == nil || err.Error() != "Missing peer objects for upgrade CC proposal" {
261+
t.Fatal("Missing peer objects validation is not working as expected")
262+
}
263+
}
264+
196265
type mockReader struct {
197266
err error
198267
}

pkg/fabric-txn/admin/transactionconfig.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,40 @@ func SendInstantiateCC(channel fab.Channel, chainCodeID string, args []string,
7777
return nil
7878
}
7979

80+
// SendUpgradeCC Sends upgrade CC proposal to one or more endorsing peers
81+
func SendUpgradeCC(channel fab.Channel, chainCodeID string, args []string,
82+
chaincodePath string, chaincodeVersion string, chaincodePolicy *common.SignaturePolicyEnvelope, targets []apitxn.ProposalProcessor, eventHub fab.EventHub) error {
83+
84+
transactionProposalResponse, txID, err := channel.SendUpgradeProposal(chainCodeID,
85+
args, chaincodePath, chaincodeVersion, chaincodePolicy, targets)
86+
if err != nil {
87+
return fmt.Errorf("SendUpgradeProposal returned error: %v", err)
88+
}
89+
90+
for _, v := range transactionProposalResponse {
91+
if v.Err != nil {
92+
return fmt.Errorf("SendUpgradeProposal Endorser %s returned error: %v", v.Endorser, v.Err)
93+
}
94+
logger.Debug("SendUpgradeProposal Endorser '%s' returned ProposalResponse status:%v\n", v.Endorser, v.Status)
95+
}
96+
97+
// Register for commit event
98+
done, fail := internal.RegisterTxEvent(txID, eventHub)
99+
100+
if _, err = internal.CreateAndSendTransaction(channel, transactionProposalResponse); err != nil {
101+
return fmt.Errorf("CreateTransaction returned error: %v", err)
102+
}
103+
104+
select {
105+
case <-done:
106+
case <-fail:
107+
return fmt.Errorf("upgradeCC Error received from eventhub for txid(%s) error(%v)", txID, fail)
108+
case <-time.After(time.Second * 30):
109+
return fmt.Errorf("upgradeCC Didn't receive block event for txid(%s)", txID)
110+
}
111+
return nil
112+
}
113+
80114
// CreateOrUpdateChannel creates a channel if it does not exist or updates a channel
81115
// if it does and a different channelConfig is used
82116
func CreateOrUpdateChannel(client fab.FabricClient, ordererUser ca.User, orgUser ca.User, channel fab.Channel, channelConfig string) error {

test/integration/base_test_setup.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,22 @@ func (setup *BaseSetupImpl) InstantiateCC(chainCodeID string, chainCodePath stri
184184
return nil
185185
}
186186

187+
// UpgradeCC ...
188+
func (setup *BaseSetupImpl) UpgradeCC(chainCodeID string, chainCodePath string, chainCodeVersion string, args []string) error {
189+
// InstantiateCC requires AdminUser privileges so setting user context with Admin User
190+
setup.Client.SetUserContext(setup.AdminUser)
191+
192+
// must reset client user context to normal user once done with Admin privilieges
193+
defer setup.Client.SetUserContext(setup.NormalUser)
194+
195+
chaincodePolicy := cauthdsl.SignedByMspMember(setup.Client.UserContext().MspID())
196+
197+
if err := admin.SendUpgradeCC(setup.Channel, chainCodeID, args, chainCodePath, chainCodeVersion, chaincodePolicy, []apitxn.ProposalProcessor{setup.Channel.PrimaryPeer()}, setup.EventHub); err != nil {
198+
return err
199+
}
200+
return nil
201+
}
202+
187203
// InstallCC ...
188204
func (setup *BaseSetupImpl) InstallCC(chainCodeID string, chainCodePath string, chainCodeVersion string, chaincodePackage []byte) error {
189205
// installCC requires AdminUser privileges so setting user context with Admin User
@@ -229,6 +245,30 @@ func (setup *BaseSetupImpl) InstallAndInstantiateExampleCC() error {
229245
return setup.InstantiateCC(setup.ChainCodeID, chainCodePath, chainCodeVersion, args)
230246
}
231247

248+
// UpgradeExampleCC ..
249+
func (setup *BaseSetupImpl) UpgradeExampleCC() error {
250+
251+
chainCodePath := "github.com/example_cc"
252+
chainCodeVersion := "v1"
253+
254+
if setup.ChainCodeID == "" {
255+
setup.ChainCodeID = GenerateRandomID()
256+
}
257+
258+
if err := setup.InstallCC(setup.ChainCodeID, chainCodePath, chainCodeVersion, nil); err != nil {
259+
return err
260+
}
261+
262+
var args []string
263+
args = append(args, "init")
264+
args = append(args, "a")
265+
args = append(args, "200")
266+
args = append(args, "b")
267+
args = append(args, "400")
268+
269+
return setup.UpgradeCC(setup.ChainCodeID, chainCodePath, chainCodeVersion, args)
270+
}
271+
232272
// Query ...
233273
func (setup *BaseSetupImpl) Query(channelID string, chainCodeID string, fcn string, args []string) (string, error) {
234274
return fabricTxn.QueryChaincode(setup.Client, setup.Channel, chainCodeID, fcn, args)

test/integration/end_to_end_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,22 @@ func TestChainCodeInvoke(t *testing.T) {
3434
t.Fatalf("InstallAndInstantiateExampleCC return error: %v", err)
3535
}
3636

37+
if err := testSetup.UpgradeExampleCC(); err != nil {
38+
t.Fatalf("UpgradeExampleCC return error: %v", err)
39+
}
40+
3741
// Get Query value before invoke
3842
value, err := testSetup.QueryAsset()
3943
if err != nil {
4044
t.Fatalf("getQueryValue return error: %v", err)
4145
}
4246
fmt.Printf("*** QueryValue before invoke %s\n", value)
4347

48+
// Check the Query value equals upgrade arguments (400)
49+
if value != "400" {
50+
t.Fatalf("UpgradeExampleCC was failed, QueryValue doesn't match upgrade arguments")
51+
}
52+
4453
eventID := "test([a-zA-Z]+)"
4554

4655
// Register callback for chaincode event

0 commit comments

Comments
 (0)