func (s *SmartContract) NotifyCommitResult(ctx contractapi.TransactionContextInterface, proposalID string) error {
	proposal, err := s.GetProposal(ctx, proposalID)
	if err != nil {
		return fmt.Errorf("failed to get proposal: %v", err)
	}
	if proposal.Status != Approved {
		return fmt.Errorf("proposal is not yet approved or already committed")
	}
	proposal.Status = Committed
	if err = s.putProposal(ctx, proposal); err != nil {
		return fmt.Errorf("failed to put proposal: %v", err)
	}
	update, err := base64.StdEncoding.DecodeString(proposal.Artifacts.ConfigUpdate)
	if err != nil {
		return fmt.Errorf("error happened decoding the configUpdate base64 string: %v", err)
	}
	unmarshaledUpdate := common.ConfigUpdate{}
	if err := proto.Unmarshal(update, &unmarshaledUpdate); err != nil {
		return fmt.Errorf("error happened decoding common.ConfigUpdate: %v", err)
	}
	configGroups := unmarshaledUpdate.GetWriteSet().GetGroups()
	log.Printf("ProposalID: %v, Channel: %v", proposalID, proposal.ChannelID)
	for key, group := range configGroups {
		if key == "Application" || key == "Consortiums" {
			organizations := []string{}
			organizationsGroup := group.Groups
			if key == "Consortiums" {
				if len(group.Groups) == 0 {
					break
				}
				for _, consortium := range group.Groups {
					organizationsGroup = consortium.Groups
					continue
				}
			}
			for org := range organizationsGroup {
				organizations = append(organizations, org)
			}
			log.Printf("Orgs: %v", organizations)
			channelExists, err := s.ChannelExists(ctx, proposal.ChannelID)
			if err != nil {
				return fmt.Errorf("error happend querying ChannelExists: %v", err)
			}
			if channelExists {
				err = s.SetOrganizations(ctx, proposal.ChannelID, organizations)
				if err != nil {
					return fmt.Errorf("error happend updating channel info: %v", err)
				}
			} else {
				err = s.CreateChannel(ctx, proposal.ChannelID, "", organizations)
				if err != nil {
					return fmt.Errorf("error happend creating channel info: %v", err)
				}
			}
			if err = ctx.GetStub().SetEvent(fmt.Sprintf("%s.%s", UpdateConfigEvent, proposalID), []byte(proposalID)); err != nil {
				return fmt.Errorf("error happened emitting event: %v", err)
			}
		}
	}
	return nil
}
NotifyCommitResult records the result of the commit for the channel update proposal.
This function records the vote as a state into the ledger.
Also, this changes the status of the proposal from approved to committed.
If the status is changed, this updates channel info in the states in the ledger based on the committed channel update.

Arguments:
0: proposalID - the ID for voting for the channel update proposal
1: signature - the base64 string representation of common.ConfigSignature for signing by the creator to the ConfigUpdate

Returns:
0: error

Events:
(if the channel info is updated)
name: UpdateConfigEvent(<proposalID>)
payload: proposalID
package chaincode
import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric-contract-api-go/contractapi"
	"github.com/hyperledger/fabric-protos-go/common"
	"github.com/hyperledger/fabric-protos-go/msp"
)
type Proposal struct {
	ObjectType string `json:"docType"`
	ID string `json:"ID"`
	ChannelID string `json:"channelID"`
	Description string `json:"description,omitempty" metadata:",optional"`
	Creator string `json:"creator"`
	Action string `json:"action"`
	Status string `json:"status"`
	OpsProfile interface{} `json:"opsProfile"`
	Artifacts Artifacts `json:"artifacts"`
}
type Artifacts struct {
	ConfigUpdate string `json:"configUpdate"`
	Signatures map[string]string `json:"signatures,omitempty" metadata:",optional"`
}
type ProposalInput struct {
	ID           string      `json:"ID"`
	ChannelID    string      `json:"channelID"`
	Description  string      `json:"description,omitempty" metadata:",optional"`
	Action       string      `json:"action,omitempty" metadata:",optional"`
	OpsProfile   interface{} `json:"opsProfile"`
	ConfigUpdate string      `json:"configUpdate"`
	Signature    string      `json:"signature"`
}
const (
	ProposalObjectType = "proposal"
)
const (
	Proposed  = "proposed"
	Approved  = "approved"
	Committed = "committed"
)
const (
	ALL      = "all"
	MAJORITY = "majority"
)
type EventDetail struct {
	ProposalID       string   `json:"proposalID"`
	OperationTargets []string `json:"operationTargets"`
}
const (
	NewProposalEvent         = "newProposalEvent"
	DeleteProposalEvent      = "deleteProposalEvent"
	NewVoteEvent             = "newVoteEvent"
	ReadyToUpdateConfigEvent = "readyToUpdateConfigEvent"
	UpdateConfigEvent        = "updateConfigEvent"
)
const (
	UpdateAction   = "update"
	CreationAction = "create"
)
var (
	ErrProposalNotFound = fmt.Errorf("proposal not found")
	ErrProposalIDAreadyInUse = fmt.Errorf("proposalID already in use")
	ErrInconsistentChannelID = fmt.Errorf("channel ID is inconsistent with the ID in the artifacts")
)
func (s *SmartContract) RequestProposal(ctx contractapi.TransactionContextInterface, input ProposalInput) (string, error) {
	proposalID := input.ID
	action := input.Action
	if action == "" {
		action = UpdateAction
	}
	if action != UpdateAction && action != CreationAction {
		return "", fmt.Errorf("incorrect operation type - expecting %v or %v", UpdateAction, CreationAction)
	}
	if err := validateConfigUpdate(input); err != nil {
		return "", err
	}
	if err := validateConfigSignature(input.Signature); err != nil {
		return "", err
	}
	if p, _ := s.GetProposal(ctx, proposalID); p != nil {
		return "", ErrProposalIDAreadyInUse
	}
	mspID, err := s.getMSPID(ctx)
	if err != nil {
		return "", fmt.Errorf("failed to get MSP ID: %v", err)
	}
	signatures := make(map[string]string)
	signatures[mspID] = input.Signature
	proposal := &Proposal{
		ObjectType:  ProposalObjectType,
		ID:          input.ID,
		ChannelID:   input.ChannelID,
		Description: input.Description,
		Creator:     mspID,
		Action:      action,
		Status:      Proposed,
		OpsProfile:  input.OpsProfile,
		Artifacts: Artifacts{
			ConfigUpdate: input.ConfigUpdate,
			Signatures:   signatures,
		},
	}
	if err = s.putProposal(ctx, proposal); err != nil {
		return "", fmt.Errorf("failed to put the proposal: %v", err)
	}
	if err = ctx.GetStub().SetEvent(fmt.Sprintf("%s.%s", NewProposalEvent, proposalID), []byte(proposalID)); err != nil {
		return "", fmt.Errorf("error happened emitting event: %v", err)
	}
	return proposalID, nil
}
func (s *SmartContract) Vote(ctx contractapi.TransactionContextInterface, proposalID, signature string) error {
	if err := validateConfigSignature(signature); err != nil {
		return err
	}
	mspID, err := s.getMSPID(ctx)
	if err != nil {
		return fmt.Errorf("failed to get MSP ID: %v", err)
	}
	proposal, err := s.GetProposal(ctx, proposalID)
	if err != nil {
		return fmt.Errorf("failed to get proposal: %v", err)
	}
	if proposal.Artifacts.Signatures == nil {
		proposal.Artifacts.Signatures = make(map[string]string)
	}
	proposal.Artifacts.Signatures[mspID] = signature
	eventName := fmt.Sprintf("%s.%s", NewVoteEvent, proposalID)
	eventPayload := []byte(proposalID)
	if proposal.Status == Proposed {
		var satisfied = false
		if satisfied, err = s.passedByVoting(ctx, proposal); err != nil {
			return fmt.Errorf("fail to check whether the votes passed: %v", err)
		}
		if satisfied {
			proposal.Status = Approved
			eventDetail := EventDetail{
				ProposalID:       proposalID,
				OperationTargets: []string{mspID},
			}
			eventDetailJSON, err := json.Marshal(eventDetail)
			if err != nil {
				return fmt.Errorf("error happened creating event detail: %v", err)
			}
			eventName = fmt.Sprintf("%s.%s", ReadyToUpdateConfigEvent, proposalID)
			eventPayload = []byte(eventDetailJSON)
		}
	}
	if err = ctx.GetStub().SetEvent(eventName, eventPayload); err != nil {
		return fmt.Errorf("error happened emitting event: %v", err)
	}
	if err = s.putProposal(ctx, proposal); err != nil {
		return fmt.Errorf("failed to put the proposal: %v", err)
	}
	return nil
}