Skip to content

Commit

Permalink
[FAB-3783] go-sdk chaincode upgrade support
Browse files Browse the repository at this point in the history
Change-Id: I0455c4dfed54d0f9f4f1e4d3ca4e32f2dd8911ee
Signed-off-by: bryan-huangyan <bryan.huang.yan@gmail.com>
  • Loading branch information
bryan-huangyan committed Sep 22, 2017
1 parent 9cde6ca commit 20fb840
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 0 deletions.
1 change: 1 addition & 0 deletions api/apifabclient/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Channel interface {
IsInitialized() bool
LoadConfigUpdateEnvelope(data []byte) error
SendInstantiateProposal(chaincodeName string, args []string, chaincodePath string, chaincodeVersion string, chaincodePolicy *common.SignaturePolicyEnvelope, targets []txn.ProposalProcessor) ([]*txn.TransactionProposalResponse, txn.TransactionID, error)
SendUpgradeProposal(chaincodeName string, args []string, chaincodePath string, chaincodeVersion string, chaincodePolicy *common.SignaturePolicyEnvelope, targets []txn.ProposalProcessor) ([]*txn.TransactionProposalResponse, txn.TransactionID, error)

// Network
// TODO: Use PeerEndorser
Expand Down
69 changes: 69 additions & 0 deletions pkg/fabric-client/channel/txnsender.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,75 @@ func (c *Channel) SendInstantiateProposal(chaincodeName string,
return transactionProposalResponse, txnID, err
}

// SendUpgradeProposal sends an upgrade proposal to one or more endorsing peers.
// chaincodeName: required - The name of the chain.
// args: optional - string Array arguments specific to the chaincode being upgraded
// chaincodePath: required - string of the path to the location of the source code of the chaincode
// chaincodeVersion: required - string of the version of the chaincode
func (c *Channel) SendUpgradeProposal(chaincodeName string,
args []string, chaincodePath string, chaincodeVersion string,
chaincodePolicy *common.SignaturePolicyEnvelope, targets []apitxn.ProposalProcessor) ([]*apitxn.TransactionProposalResponse, apitxn.TransactionID, error) {

if chaincodeName == "" {
return nil, apitxn.TransactionID{}, fmt.Errorf("Missing 'chaincodeName' parameter")
}
if chaincodePath == "" {
return nil, apitxn.TransactionID{}, fmt.Errorf("Missing 'chaincodePath' parameter")
}
if chaincodeVersion == "" {
return nil, apitxn.TransactionID{}, fmt.Errorf("Missing 'chaincodeVersion' parameter")
}
if chaincodePolicy == nil {
return nil, apitxn.TransactionID{}, fmt.Errorf("Missing 'chaincodePolicy' parameter")
}

// TODO: We should validate that targets are added to the channel.
if targets == nil || len(targets) < 1 {
return nil, apitxn.TransactionID{}, fmt.Errorf("Missing peer objects for upgrade CC proposal")
}

argsArray := make([][]byte, len(args))
for i, arg := range args {
argsArray[i] = []byte(arg)
}

ccds := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{
Type: pb.ChaincodeSpec_GOLANG, ChaincodeId: &pb.ChaincodeID{Name: chaincodeName, Path: chaincodePath, Version: chaincodeVersion},
Input: &pb.ChaincodeInput{Args: argsArray}}}

if c.clientContext.UserContext() == nil {
return nil, apitxn.TransactionID{}, fmt.Errorf("User context needs to be set")
}
creator, err := c.clientContext.UserContext().Identity()
if err != nil {
return nil, apitxn.TransactionID{}, fmt.Errorf("Error getting creator: %v", err)
}
chaincodePolicyBytes, err := protos_utils.Marshal(chaincodePolicy)
if err != nil {
return nil, apitxn.TransactionID{}, err
}
// create a proposal from a chaincodeDeploymentSpec
proposal, txID, err := protos_utils.CreateUpgradeProposalFromCDS(c.Name(), ccds, creator, chaincodePolicyBytes, []byte("escc"), []byte("vscc"))
if err != nil {
return nil, apitxn.TransactionID{}, fmt.Errorf("Could not create chaincode Upgrade proposal, err %s", err)
}

signedProposal, err := c.signProposal(proposal)
if err != nil {
return nil, apitxn.TransactionID{}, err
}

txnID := apitxn.TransactionID{ID: txID} // Nonce is missing

transactionProposalResponse, err := txnproc.SendTransactionProposalToProcessors(&apitxn.TransactionProposal{
SignedProposal: signedProposal,
Proposal: proposal,
TxnID: txnID,
}, targets)

return transactionProposalResponse, txnID, err
}

// SignPayload ... TODO.
func (c *Channel) SignPayload(payload []byte) (*fab.SignedEnvelope, error) {
//Get user info
Expand Down
69 changes: 69 additions & 0 deletions pkg/fabric-client/channel/txnsender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ func TestCreateTransaction(t *testing.T) {
//TODO: Need actual sample payload for success case

}

func TestSendInstantiateProposal(t *testing.T) {
//Setup channel
client := mocks.NewMockClient()
Expand Down Expand Up @@ -193,6 +194,74 @@ func TestSendInstantiateProposal(t *testing.T) {
}
}

func TestSendUpgradeProposal(t *testing.T) {
//Setup channel
client := mocks.NewMockClient()
user := mocks.NewMockUserWithMSPID("test", "1234")
cryptoSuite := &mocks.MockCryptoSuite{}
client.SaveUserToStateStore(user, true)
client.SetCryptoSuite(cryptoSuite)
client.SetUserContext(user)
channel, _ := NewChannel("testChannel", client)

mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
proc := mock_apitxn.NewMockProposalProcessor(mockCtrl)

tp := apitxn.TransactionProposal{SignedProposal: &pb.SignedProposal{}}
tpr := apitxn.TransactionProposalResult{Endorser: "example.com", Status: 99, Proposal: tp, ProposalResponse: nil}

proc.EXPECT().ProcessTransactionProposal(gomock.Any()).Return(tpr, nil)
targets := []apitxn.ProposalProcessor{proc}

//Add a Peer
peer := mocks.MockPeer{MockName: "Peer1", MockURL: "http://peer1.com", MockRoles: []string{}, MockCert: nil}
channel.AddPeer(&peer)

tresponse, txnid, err := channel.SendUpgradeProposal("", nil, "",
"", cauthdsl.SignedByMspMember("Org1MSP"), targets)

if err == nil || err.Error() != "Missing 'chaincodeName' parameter" {
t.Fatal("Validation for chain code name parameter for send Upgrade Proposal failed")
}

tresponse, txnid, err = channel.SendUpgradeProposal("qscc", nil, "",
"", cauthdsl.SignedByMspMember("Org1MSP"), targets)

tresponse, txnid, err = channel.SendUpgradeProposal("qscc", nil, "",
"", cauthdsl.SignedByMspMember("Org1MSP"), targets)

if err == nil || err.Error() != "Missing 'chaincodePath' parameter" {
t.Fatal("Validation for chain code path for send Upgrade Proposal failed")
}

tresponse, txnid, err = channel.SendUpgradeProposal("qscc", nil, "test",
"", cauthdsl.SignedByMspMember("Org1MSP"), targets)

if err == nil || err.Error() != "Missing 'chaincodeVersion' parameter" {
t.Fatal("Validation for chain code version for send Upgrade Proposal failed")
}

tresponse, txnid, err = channel.SendUpgradeProposal("qscc", nil, "test",
"2", nil, nil)
if err == nil || err.Error() != "Missing 'chaincodePolicy' parameter" {
t.Fatal("Validation for chain code policy for send Upgrade Proposal failed")
}

tresponse, txnid, err = channel.SendUpgradeProposal("qscc", nil, "test",
"2", cauthdsl.SignedByMspMember("Org1MSP"), targets)

if err != nil || len(tresponse) == 0 || txnid.ID == "" {
t.Fatal("Send Upgrade Proposal Test failed")
}

tresponse, txnid, err = channel.SendUpgradeProposal("qscc", nil, "test",
"2", cauthdsl.SignedByMspMember("Org1MSP"), nil)
if err == nil || err.Error() != "Missing peer objects for upgrade CC proposal" {
t.Fatal("Missing peer objects validation is not working as expected")
}
}

type mockReader struct {
err error
}
Expand Down
34 changes: 34 additions & 0 deletions pkg/fabric-txn/admin/transactionconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,40 @@ func SendInstantiateCC(channel fab.Channel, chainCodeID string, args []string,
return nil
}

// SendUpgradeCC Sends upgrade CC proposal to one or more endorsing peers
func SendUpgradeCC(channel fab.Channel, chainCodeID string, args []string,
chaincodePath string, chaincodeVersion string, chaincodePolicy *common.SignaturePolicyEnvelope, targets []apitxn.ProposalProcessor, eventHub fab.EventHub) error {

transactionProposalResponse, txID, err := channel.SendUpgradeProposal(chainCodeID,
args, chaincodePath, chaincodeVersion, chaincodePolicy, targets)
if err != nil {
return fmt.Errorf("SendUpgradeProposal returned error: %v", err)
}

for _, v := range transactionProposalResponse {
if v.Err != nil {
return fmt.Errorf("SendUpgradeProposal Endorser %s returned error: %v", v.Endorser, v.Err)
}
logger.Debug("SendUpgradeProposal Endorser '%s' returned ProposalResponse status:%v\n", v.Endorser, v.Status)
}

// Register for commit event
done, fail := internal.RegisterTxEvent(txID, eventHub)

if _, err = internal.CreateAndSendTransaction(channel, transactionProposalResponse); err != nil {
return fmt.Errorf("CreateTransaction returned error: %v", err)
}

select {
case <-done:
case <-fail:
return fmt.Errorf("upgradeCC Error received from eventhub for txid(%s) error(%v)", txID, fail)
case <-time.After(time.Second * 30):
return fmt.Errorf("upgradeCC Didn't receive block event for txid(%s)", txID)
}
return nil
}

// CreateOrUpdateChannel creates a channel if it does not exist or updates a channel
// if it does and a different channelConfig is used
func CreateOrUpdateChannel(client fab.FabricClient, ordererUser ca.User, orgUser ca.User, channel fab.Channel, channelConfig string) error {
Expand Down
40 changes: 40 additions & 0 deletions test/integration/base_test_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,22 @@ func (setup *BaseSetupImpl) InstantiateCC(chainCodeID string, chainCodePath stri
return nil
}

// UpgradeCC ...
func (setup *BaseSetupImpl) UpgradeCC(chainCodeID string, chainCodePath string, chainCodeVersion string, args []string) error {
// InstantiateCC requires AdminUser privileges so setting user context with Admin User
setup.Client.SetUserContext(setup.AdminUser)

// must reset client user context to normal user once done with Admin privilieges
defer setup.Client.SetUserContext(setup.NormalUser)

chaincodePolicy := cauthdsl.SignedByMspMember(setup.Client.UserContext().MspID())

if err := admin.SendUpgradeCC(setup.Channel, chainCodeID, args, chainCodePath, chainCodeVersion, chaincodePolicy, []apitxn.ProposalProcessor{setup.Channel.PrimaryPeer()}, setup.EventHub); err != nil {
return err
}
return nil
}

// InstallCC ...
func (setup *BaseSetupImpl) InstallCC(chainCodeID string, chainCodePath string, chainCodeVersion string, chaincodePackage []byte) error {
// installCC requires AdminUser privileges so setting user context with Admin User
Expand Down Expand Up @@ -229,6 +245,30 @@ func (setup *BaseSetupImpl) InstallAndInstantiateExampleCC() error {
return setup.InstantiateCC(setup.ChainCodeID, chainCodePath, chainCodeVersion, args)
}

// UpgradeExampleCC ..
func (setup *BaseSetupImpl) UpgradeExampleCC() error {

chainCodePath := "github.com/example_cc"
chainCodeVersion := "v1"

if setup.ChainCodeID == "" {
setup.ChainCodeID = GenerateRandomID()
}

if err := setup.InstallCC(setup.ChainCodeID, chainCodePath, chainCodeVersion, nil); err != nil {
return err
}

var args []string
args = append(args, "init")
args = append(args, "a")
args = append(args, "200")
args = append(args, "b")
args = append(args, "400")

return setup.UpgradeCC(setup.ChainCodeID, chainCodePath, chainCodeVersion, args)
}

// Query ...
func (setup *BaseSetupImpl) Query(channelID string, chainCodeID string, fcn string, args []string) (string, error) {
return fabricTxn.QueryChaincode(setup.Client, setup.Channel, chainCodeID, fcn, args)
Expand Down
9 changes: 9 additions & 0 deletions test/integration/end_to_end_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,22 @@ func TestChainCodeInvoke(t *testing.T) {
t.Fatalf("InstallAndInstantiateExampleCC return error: %v", err)
}

if err := testSetup.UpgradeExampleCC(); err != nil {
t.Fatalf("UpgradeExampleCC return error: %v", err)
}

// Get Query value before invoke
value, err := testSetup.QueryAsset()
if err != nil {
t.Fatalf("getQueryValue return error: %v", err)
}
fmt.Printf("*** QueryValue before invoke %s\n", value)

// Check the Query value equals upgrade arguments (400)
if value != "400" {
t.Fatalf("UpgradeExampleCC was failed, QueryValue doesn't match upgrade arguments")
}

eventID := "test([a-zA-Z]+)"

// Register callback for chaincode event
Expand Down

0 comments on commit 20fb840

Please sign in to comment.