Skip to content

Commit f601374

Browse files
committed
[FAB-6230] Resource utilities for peer CLI
This change set simply adds resource handling utilities for the next change set, to make the code review easier. Change-Id: Iaec3c6abf8224e75a8c6a20c36831876e53a4ad4 Signed-off-by: yacovm <yacovm@il.ibm.com>
1 parent 7b452c7 commit f601374

File tree

4 files changed

+737
-18
lines changed

4 files changed

+737
-18
lines changed

peer/chaincode/resources.go

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
/*
2+
Copyright IBM Corp. 2016-2017 All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package chaincode
8+
9+
import (
10+
"bytes"
11+
"fmt"
12+
"strings"
13+
"time"
14+
15+
"github.com/golang/protobuf/proto"
16+
"github.com/hyperledger/fabric/common/resourcesconfig"
17+
"github.com/hyperledger/fabric/common/util"
18+
"github.com/hyperledger/fabric/core/chaincode/shim"
19+
"github.com/hyperledger/fabric/core/scc/lscc"
20+
"github.com/hyperledger/fabric/protos/common"
21+
"github.com/hyperledger/fabric/protos/peer"
22+
"github.com/hyperledger/fabric/protos/utils"
23+
"github.com/pkg/errors"
24+
"golang.org/x/net/context"
25+
)
26+
27+
type channelVersion uint8
28+
29+
const (
30+
v1 = iota
31+
v11
32+
)
33+
34+
// SignatureSupport creates signature headers, signs messages,
35+
// and also serializes its identity to bytes
36+
type SignatureSupport interface {
37+
// Sign the message
38+
Sign(msg []byte) ([]byte, error)
39+
40+
// Serialize converts an identity to bytes
41+
Serialize() ([]byte, error)
42+
43+
// NewSignatureHeader creates a new signature header
44+
NewSignatureHeader() (*common.SignatureHeader, error)
45+
}
46+
47+
// deltaComputer computes the delta from a starting config to a target config
48+
type deltaComputer func(original, updated *common.Config) (*common.ConfigUpdate, error)
49+
50+
type sendInitTransaction func() error
51+
52+
type ccUpdate struct {
53+
policy *common.SignaturePolicyEnvelope
54+
computeDelta deltaComputer
55+
SignatureSupport
56+
ccName string
57+
oldConfig *common.Config
58+
newConfig *common.Config
59+
chainID string
60+
hash []byte
61+
validation string
62+
endorsement string
63+
version string
64+
}
65+
66+
// assembleProposal assembles a SignedProposal given parameters
67+
func assembleProposal(ss SignatureSupport, channel string, targetCC string, function string, args ...string) (*peer.SignedProposal, error) {
68+
var invocation *peer.ChaincodeInvocationSpec
69+
if len(args) == 0 {
70+
invocation = &peer.ChaincodeInvocationSpec{
71+
ChaincodeSpec: &peer.ChaincodeSpec{
72+
Type: peer.ChaincodeSpec_Type(peer.ChaincodeSpec_Type_value["GOLANG"]),
73+
ChaincodeId: &peer.ChaincodeID{Name: targetCC},
74+
Input: &peer.ChaincodeInput{Args: [][]byte{[]byte(function)}},
75+
},
76+
}
77+
} else {
78+
invocation = &peer.ChaincodeInvocationSpec{
79+
ChaincodeSpec: &peer.ChaincodeSpec{
80+
Type: peer.ChaincodeSpec_Type(peer.ChaincodeSpec_Type_value["GOLANG"]),
81+
ChaincodeId: &peer.ChaincodeID{Name: targetCC},
82+
Input: &peer.ChaincodeInput{Args: [][]byte{[]byte(function), []byte(args[0])}},
83+
},
84+
}
85+
}
86+
87+
var prop *peer.Proposal
88+
c, err := ss.Serialize()
89+
if err != nil {
90+
return nil, err
91+
}
92+
prop, _, err = utils.CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, channel, invocation, c)
93+
if err != nil {
94+
return nil, errors.New(fmt.Sprintf("Cannot create proposal, due to %s", err))
95+
}
96+
return utils.GetSignedProposal(prop, ss)
97+
}
98+
99+
// fetchCCID fetches the ID of the chaincode from LSCC
100+
func fetchCCID(ss SignatureSupport, ec peer.EndorserClient, name, version string) ([]byte, error) {
101+
sp, err := assembleProposal(ss, "", "lscc", lscc.GETINSTALLEDCHAINCODES)
102+
if err != nil {
103+
return nil, err
104+
}
105+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
106+
defer cancel()
107+
resp, err := ec.ProcessProposal(ctx, sp)
108+
if err != nil {
109+
return nil, err
110+
}
111+
if resp.Response.Status != shim.OK {
112+
return nil, errors.New(resp.Response.Message)
113+
}
114+
qr := &peer.ChaincodeQueryResponse{}
115+
if err := proto.Unmarshal(resp.Response.Payload, qr); err != nil {
116+
return nil, err
117+
}
118+
for _, cc := range qr.Chaincodes {
119+
if cc.Name == name && cc.Version == version {
120+
return cc.Id, nil
121+
}
122+
}
123+
return nil, errors.Errorf("chaincode with name %s and version %s wasn't found", name, version)
124+
}
125+
126+
// fetchResourceConfig fetches the resource config from the peer, if applicable.
127+
// else, it returns the channel version to be v1.0, or an error upon failure
128+
func fetchResourceConfig(ec peer.EndorserClient, ss SignatureSupport, channel string) (channelVersion, *common.Config, error) {
129+
sp, err := assembleProposal(ss, channel, "cscc", "GetConfigTree", channel)
130+
if err != nil {
131+
logger.Warning("Cannot determine peer version, Proposal assembly failed:", err)
132+
return 0, nil, err
133+
}
134+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
135+
defer cancel()
136+
resp, err := ec.ProcessProposal(ctx, sp)
137+
138+
if err != nil {
139+
// ProcessProposal may return:
140+
// Case 1: An error, that isn't associated with CSCC chaincode execution (i.e RPC error)
141+
if !strings.Contains(err.Error(), "chaincode") {
142+
logger.Warning("Cannot determine peer version, CSCC returned:", err)
143+
return 0, nil, err
144+
}
145+
// Case 2: An error, that is associated with CSCC chaincode execution
146+
logger.Debug("Peer returned:", err.Error(), "assuming it to be an lscc channel")
147+
return v1, nil, nil
148+
}
149+
150+
if resp == nil || resp.Response == nil {
151+
return 0, nil, errors.Errorf("empty response")
152+
}
153+
154+
// Case 3: A response, with response of a non OK status
155+
if resp.Response.Status != shim.OK {
156+
logger.Debug("Peer returned:", resp.Response, "assuming it to be an lscc channel")
157+
return v1, nil, nil
158+
}
159+
conf := &peer.ConfigTree{}
160+
161+
// Case 4: A response, with an OK status but with an invalid Config payload
162+
if err := proto.Unmarshal(resp.Response.Payload, conf); err != nil || conf.ResourcesConfig.ChannelGroup == nil {
163+
logger.Warning("Peer returned malformed config", conf)
164+
return v1, nil, nil
165+
}
166+
167+
// case 5: A response with an OK status but with a nil groups inside the channel group,
168+
// or with a missing chaincodeGroupKey
169+
if g := conf.ResourcesConfig.ChannelGroup.Groups; g == nil || g[resourcesconfig.ChaincodesGroupKey] == nil {
170+
return v1, nil, nil
171+
}
172+
173+
// Case 6: A response, with an OK status and a valid config
174+
logger.Debug("Peer returned config, assuming it is a config based lifecycle channel")
175+
return v11, conf.ResourcesConfig, nil
176+
}
177+
178+
func ccGroup(ccName string, version string, validation string, endorsement string, hash []byte, modpolicies map[string]string, policy *common.SignaturePolicyEnvelope) *common.ConfigGroup {
179+
logger.Infof("creating ccGroup using %s and %s", validation, endorsement)
180+
var vsccArg []byte
181+
if validation == "" {
182+
validation = "vscc"
183+
}
184+
if endorsement == "" {
185+
endorsement = "escc"
186+
}
187+
if validation == "static-endorsement-policy" {
188+
vsccArg = utils.MarshalOrPanic(&peer.VSCCArgs{
189+
EndorsementPolicyRef: fmt.Sprintf("/Resources/Chaincodes/%s/Endorsement", ccName),
190+
})
191+
}
192+
if validation == "vscc" {
193+
logger.Infof("Setting VSCC arg to simple policy, using %s and %s", validation, endorsement)
194+
vsccArg = utils.MarshalOrPanic(policy)
195+
}
196+
return &common.ConfigGroup{
197+
Policies: map[string]*common.ConfigPolicy{
198+
// TODO: make a constant in some other package
199+
"Endorsement": {
200+
ModPolicy: modpolicies["[Policy] Endorsement"],
201+
Policy: &common.Policy{
202+
Type: int32(common.Policy_SIGNATURE),
203+
Value: utils.MarshalOrPanic(policy),
204+
},
205+
},
206+
},
207+
ModPolicy: modpolicies["Base"],
208+
Values: map[string]*common.ConfigValue{
209+
// TODO: make a constant in some other package
210+
"ChaincodeIdentifier": {
211+
ModPolicy: modpolicies["ChaincodeIdentifier"],
212+
Value: utils.MarshalOrPanic(&peer.ChaincodeIdentifier{
213+
Version: version,
214+
Hash: hash,
215+
}),
216+
},
217+
// TODO: make a constant in some other package
218+
"ChaincodeValidation": {
219+
ModPolicy: modpolicies["ChaincodeValidation"],
220+
Value: utils.MarshalOrPanic(&peer.ChaincodeValidation{
221+
Name: validation,
222+
Argument: vsccArg,
223+
}),
224+
},
225+
// TODO: make a constant in some other package
226+
"ChaincodeEndorsement": {
227+
ModPolicy: modpolicies["ChaincodeEndorsement"],
228+
Value: utils.MarshalOrPanic(&peer.ChaincodeEndorsement{
229+
Name: endorsement,
230+
}),
231+
},
232+
},
233+
}
234+
}
235+
236+
func (update ccUpdate) addChaincode() {
237+
ccGrp, exists := update.newConfig.ChannelGroup.Groups[resourcesconfig.ChaincodesGroupKey]
238+
if !exists {
239+
// We shouldn't reach here, because we classify such a resource config as a v1 channel,
240+
// thus we shouldn't even attempt to modify the chaincode definitions, hence not reach this function.
241+
logger.Panic("Programming error: chaincodes group doesn't exist")
242+
}
243+
// In case no Groups, make our own
244+
if ccGrp.Groups == nil {
245+
logger.Debug("Creating a Groups key for", resourcesconfig.ChaincodesGroupKey)
246+
ccGrp.Groups = make(map[string]*common.ConfigGroup)
247+
}
248+
modPolicies := getModPolicies(ccGrp, update)
249+
ccg := ccGroup(update.ccName, update.version, update.validation, update.endorsement, update.hash, modPolicies, update.policy)
250+
ccGrp.Groups[update.ccName] = ccg
251+
}
252+
253+
func (update ccUpdate) newConfigUpdate(cfgUpdate *common.ConfigUpdate) *common.ConfigUpdateEnvelope {
254+
newConfigUpdateEnv := &common.ConfigUpdateEnvelope{
255+
ConfigUpdate: utils.MarshalOrPanic(cfgUpdate),
256+
}
257+
update.appendSignature(newConfigUpdateEnv)
258+
return newConfigUpdateEnv
259+
}
260+
261+
func (update ccUpdate) appendSignature(env *common.ConfigUpdateEnvelope) *common.ConfigUpdateEnvelope {
262+
sigHdr, err := update.NewSignatureHeader()
263+
if err != nil {
264+
logger.Panic(err)
265+
}
266+
// First iterate over the signatures and see if we already signed this envelope
267+
for _, sig := range env.Signatures {
268+
sh, err := utils.GetSignatureHeader(sig.SignatureHeader)
269+
if err != nil {
270+
logger.Panicf("signature header invalid: %v", err)
271+
}
272+
logger.Info("Found our own signature header, skipping appending our signature...")
273+
if bytes.Equal(sigHdr.Creator, sh.Creator) {
274+
return env
275+
}
276+
}
277+
configSig := &common.ConfigSignature{
278+
SignatureHeader: utils.MarshalOrPanic(sigHdr),
279+
}
280+
configSig.Signature, err = update.Sign(util.ConcatenateBytes(configSig.SignatureHeader, env.ConfigUpdate))
281+
if err != nil {
282+
logger.Panicf("failed signing config update: %v", err)
283+
}
284+
env.Signatures = append(env.Signatures, configSig)
285+
return env
286+
}
287+
288+
func (update ccUpdate) updateIntoEnvelope(updateEnv *common.ConfigUpdateEnvelope) *common.Envelope {
289+
env, err := utils.CreateSignedEnvelope(common.HeaderType_PEER_RESOURCE_UPDATE, update.chainID, update, updateEnv, 0, 0)
290+
if err != nil {
291+
logger.Panic(err)
292+
}
293+
return env
294+
}
295+
296+
func (update ccUpdate) buildCCUpdateEnvelope() *common.Envelope {
297+
update.newConfig = proto.Clone(update.oldConfig).(*common.Config)
298+
update.addChaincode()
299+
cfgUpdate, err := update.computeDelta(update.oldConfig, update.newConfig)
300+
if err != nil {
301+
logger.Panic(err)
302+
}
303+
cfgUpdate.ChannelId = update.chainID
304+
newConfigUpdateEnv := update.newConfigUpdate(cfgUpdate)
305+
return update.updateIntoEnvelope(newConfigUpdateEnv)
306+
}
307+
308+
func getModPolicies(ccGrp *common.ConfigGroup, update ccUpdate) map[string]string {
309+
// By default, it's the channel admins
310+
modPolicies := map[string]string{
311+
"Base": "/Channel/Application/Admins",
312+
"ChaincodeIdentifier": "/Channel/Application/Admins",
313+
"ChaincodeValidation": "/Channel/Application/Admins",
314+
"ChaincodeEndorsement": "/Channel/Application/Admins",
315+
"[Policy] Endorsement": "/Channel/Application/Admins",
316+
}
317+
// If the chaincode has already been configured before,
318+
// we would want (by default) to preserve its modification policy
319+
if oldChaincodeGroup := ccGrp.Groups[update.ccName]; oldChaincodeGroup != nil {
320+
modPolicies["Base"] = oldChaincodeGroup.ModPolicy
321+
modPolicies["ChaincodeIdentifier"] = oldChaincodeGroup.Values["ChaincodeIdentifier"].ModPolicy
322+
modPolicies["ChaincodeValidation"] = oldChaincodeGroup.Values["ChaincodeValidation"].ModPolicy
323+
modPolicies["ChaincodeEndorsement"] = oldChaincodeGroup.Values["ChaincodeEndorsement"].ModPolicy
324+
modPolicies["[Policy] Endorsement"] = oldChaincodeGroup.Policies["Endorsement"].ModPolicy
325+
}
326+
return modPolicies
327+
}

0 commit comments

Comments
 (0)