Skip to content

Commit d4967ca

Browse files
author
Srinivasan Muralidharan
committed
FAB-2958 create and sign chaincode package
https://jira.hyperledger.org/browse/FAB-2958 CLI is enhanced . create a chaincode package (extenstion to "package") . sign a previously created package (new "signpackage" subcommand) The main files affected are "package.go" and "signpackage.go". Rest of the changes to chaincode CLI files are just for API adjustment. Also added tests and cc-packaging-and-signing.rst. Change-Id: Ieb55334d33d5f03bd315f7722b02badc2010675f Signed-off-by: Srinivasan Muralidharan <muralisr@us.ibm.com>
1 parent a96c4f7 commit d4967ca

File tree

14 files changed

+726
-24
lines changed

14 files changed

+726
-24
lines changed

core/common/ccpackage/ccpackage.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,28 @@ import (
2828
"github.com/hyperledger/fabric/protos/utils"
2929
)
3030

31+
// extractSignedCCDepSpec extracts the messages from the envelope
32+
func extractSignedCCDepSpec(env *common.Envelope) (*common.ChannelHeader, *peer.SignedChaincodeDeploymentSpec, error) {
33+
p := &common.Payload{}
34+
err := proto.Unmarshal(env.Payload, p)
35+
if err != nil {
36+
return nil, nil, err
37+
}
38+
ch := &common.ChannelHeader{}
39+
err = proto.Unmarshal(p.Header.ChannelHeader, ch)
40+
if err != nil {
41+
return nil, nil, err
42+
}
43+
44+
sp := &peer.SignedChaincodeDeploymentSpec{}
45+
err = proto.Unmarshal(p.Data, sp)
46+
if err != nil {
47+
return nil, nil, err
48+
}
49+
50+
return ch, sp, nil
51+
}
52+
3153
// This file provides functions for helping with the chaincode install
3254
// package workflow. In particular
3355
// OwnerCreateSignedCCDepSpec - each owner creates signs the package using the same deploy
@@ -185,3 +207,39 @@ func OwnerCreateSignedCCDepSpec(cds *peer.ChaincodeDeploymentSpec, instPolicy *c
185207

186208
return createSignedCCDepSpec(cdsbytes, instpolicybytes, endorsements)
187209
}
210+
211+
// SignExistingPackage adds a signature to a signed package.
212+
func SignExistingPackage(env *common.Envelope, owner msp.SigningIdentity) (*common.Envelope, error) {
213+
if owner == nil {
214+
return nil, fmt.Errorf("owner not provided")
215+
}
216+
217+
ch, sdepspec, err := extractSignedCCDepSpec(env)
218+
if err != nil {
219+
return nil, err
220+
}
221+
222+
if ch == nil {
223+
return nil, fmt.Errorf("channel header not found in the envelope")
224+
}
225+
226+
if sdepspec == nil || sdepspec.ChaincodeDeploymentSpec == nil || sdepspec.InstantiationPolicy == nil || sdepspec.OwnerEndorsements == nil {
227+
return nil, fmt.Errorf("invalid signed deployment spec")
228+
}
229+
230+
// serialize the signing identity
231+
endorser, err := owner.Serialize()
232+
if err != nil {
233+
return nil, fmt.Errorf("Could not serialize the signing identity for %s, err %s", owner.GetIdentifier(), err)
234+
}
235+
236+
// sign the concatenation of cds, instpolicy and the serialized endorser identity with this endorser's key
237+
signature, err := owner.Sign(append(sdepspec.ChaincodeDeploymentSpec, append(sdepspec.InstantiationPolicy, endorser...)...))
238+
if err != nil {
239+
return nil, fmt.Errorf("Could not sign the ccpackage, err %s", err)
240+
}
241+
242+
endorsements := append(sdepspec.OwnerEndorsements, &peer.Endorsement{Signature: signature, Endorser: endorser})
243+
244+
return createSignedCCDepSpec(sdepspec.ChaincodeDeploymentSpec, sdepspec.InstantiationPolicy, endorsements)
245+
}

core/common/ccpackage/ccpackage_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,45 @@ func TestOwnerCreateSignedCCDepSpec(t *testing.T) {
6464
}
6565
}
6666

67+
func TestAddSignature(t *testing.T) {
68+
mspid, _ := localmsp.GetIdentifier()
69+
sigpolicy := createInstantiationPolicy(mspid, mspprotos.MSPRole_ADMIN)
70+
env, err := ownerCreateCCDepSpec([]byte("codepackage"), sigpolicy, signer)
71+
if err != nil || env == nil {
72+
t.Fatalf("error owner creating package %s", err)
73+
return
74+
}
75+
//add one more with the same signer (we don't have another signer to test with)
76+
env, err = SignExistingPackage(env, signer)
77+
if err != nil || env == nil {
78+
t.Fatalf("error signing existing package %s", err)
79+
return
80+
}
81+
//...and sign aother for luck
82+
env, err = SignExistingPackage(env, signer)
83+
if err != nil || env == nil {
84+
t.Fatalf("error signing existing package %s", err)
85+
return
86+
}
87+
88+
p := &common.Payload{}
89+
if err = proto.Unmarshal(env.Payload, p); err != nil {
90+
t.Fatalf("fatal error unmarshal payload")
91+
return
92+
}
93+
94+
sigdepspec := &peer.SignedChaincodeDeploymentSpec{}
95+
if err = proto.Unmarshal(p.Data, sigdepspec); err != nil || sigdepspec == nil {
96+
t.Fatalf("fatal error unmarshal sigdepspec")
97+
return
98+
}
99+
100+
if len(sigdepspec.OwnerEndorsements) != 3 {
101+
t.Fatalf("invalid number of endorsements %d", len(sigdepspec.OwnerEndorsements))
102+
return
103+
}
104+
}
105+
67106
func TestMissingSigaturePolicy(t *testing.T) {
68107
env, err := ownerCreateCCDepSpec([]byte("codepackage"), nil, signer)
69108
if err == nil || env != nil {
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
Chaincode Packaging and Signing
2+
===============================
3+
4+
Introduction
5+
------------
6+
7+
A chaincode will be placed on the file system of the peer on
8+
installation simply as a file with name
9+
``<chaincode name>.<chaincode version``. The contents of that file is
10+
called a chaincode package.
11+
12+
This document describes how a chaincode package can be created and
13+
signed from CLI. The package is used for install using the usual install
14+
procedures and not covered in this document.
15+
16+
What’s in the package ?
17+
-----------------------
18+
19+
The package consists of 3 parts \* the chaincode as defined by
20+
``ChaincodeDeploymentSpec``. This defines the code and other meta
21+
properties such as name and version \* an instantiation policy which can
22+
be syntactically described by the same policy used for endorsement and
23+
described in ``endorsement-policies.rst`` \* a set of signatures by the
24+
entities that “own” the chaincode.
25+
26+
The signatures serve the following purposes \* establish an ownership of
27+
the chaincode \* allows verification that the signatures are over the
28+
same content \* allows detection of package tampering
29+
30+
The creator of the instantiation of the chaincode on a channel is
31+
validated against the instantiation policy of the chaincode.
32+
33+
Chaincode Packaging
34+
-------------------
35+
36+
The package is created and signed using the command
37+
38+
::
39+
40+
peer chaincode package -n mycc -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02 -v 0 -s -S ccpack.out
41+
42+
where ``-s`` specifies creating the package as opposed to generating raw
43+
ChaincodeDeploymentSpec ``-S`` specifies instructs to sign the package
44+
using the Local MSP (as defined by ``localMspid`` property in
45+
``core.yaml``)
46+
47+
The ``-S`` option is optional. However if a package is created without a
48+
signature, it cannot be signed by any other owner using the
49+
``signpackage`` command in the next section.
50+
51+
Package signing
52+
---------------
53+
54+
A package can be handed over to other owners for inspection and signing.
55+
The workflow supports out of band signing of package.
56+
57+
A previously created package can be signed using the command
58+
59+
::
60+
61+
peer chaincode signpackage ccpack.out signedccpack.out
62+
63+
where ``ccpack.out`` and ``signedccpack.out`` are input and output
64+
packages respectively. ``signedccpack.out`` contains an additional
65+
signature over the package signed using the Local MSP.
66+
67+
Conclusion
68+
----------
69+
70+
The peer will support use of both raw ChaincodeDeploymentSpec and the
71+
package structure described in this document. This will allow existing
72+
commands and workflows to work which is especially useful in development
73+
and test phases.

peer/chaincode/chaincode.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,9 @@ func Cmd(cf *ChaincodeCmdFactory) *cobra.Command {
6969
chaincodeCmd.AddCommand(invokeCmd(cf))
7070
chaincodeCmd.AddCommand(queryCmd(cf))
7171
chaincodeCmd.AddCommand(upgradeCmd(cf))
72-
chaincodeCmd.AddCommand(packageCmd(cf))
72+
chaincodeCmd.AddCommand(packageCmd(cf, nil))
7373
chaincodeCmd.AddCommand(installCmd(cf))
74+
chaincodeCmd.AddCommand(signpackageCmd(cf))
7475

7576
return chaincodeCmd
7677
}

peer/chaincode/common.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func checkChaincodeCmdParams(cmd *cobra.Command) error {
133133
return fmt.Errorf("Must supply value for %s name parameter.", chainFuncName)
134134
}
135135

136-
if cmd.Name() == instantiate_cmdname || cmd.Name() == install_cmdname || cmd.Name() == upgrade_cmdname {
136+
if cmd.Name() == instantiate_cmdname || cmd.Name() == install_cmdname || cmd.Name() == upgrade_cmdname || cmd.Name() == package_cmdname {
137137
if chaincodeVersion == common.UndefinedParamValue {
138138
return fmt.Errorf("Chaincode version is not provided for %s", cmd.Name())
139139
}
@@ -198,7 +198,7 @@ func checkChaincodeCmdParams(cmd *cobra.Command) error {
198198
return errors.New("Non-empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'")
199199
}
200200
} else {
201-
if cmd == nil || cmd != chaincodeInstallCmd {
201+
if cmd == nil || (cmd != chaincodeInstallCmd && cmd != chaincodePackageCmd) {
202202
return errors.New("Empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'")
203203
}
204204
}
@@ -214,10 +214,14 @@ type ChaincodeCmdFactory struct {
214214
}
215215

216216
// InitCmdFactory init the ChaincodeCmdFactory with default clients
217-
func InitCmdFactory(isOrdererRequired bool) (*ChaincodeCmdFactory, error) {
218-
endorserClient, err := common.GetEndorserClient()
219-
if err != nil {
220-
return nil, fmt.Errorf("Error getting endorser client %s: %s", chainFuncName, err)
217+
func InitCmdFactory(isEndorserRequired, isOrdererRequired bool) (*ChaincodeCmdFactory, error) {
218+
var err error
219+
var endorserClient pb.EndorserClient
220+
if isEndorserRequired {
221+
endorserClient, err = common.GetEndorserClient()
222+
if err != nil {
223+
return nil, fmt.Errorf("Error getting endorser client %s: %s", chainFuncName, err)
224+
}
221225
}
222226

223227
signer, err := common.GetDefaultSigner()

peer/chaincode/install.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func chaincodeInstall(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory
8888

8989
var err error
9090
if cf == nil {
91-
cf, err = InitCmdFactory(false)
91+
cf, err = InitCmdFactory(true, false)
9292
if err != nil {
9393
return err
9494
}

peer/chaincode/instantiate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func instantiate(cmd *cobra.Command, cf *ChaincodeCmdFactory) (*protcommon.Envel
9999
func chaincodeDeploy(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory) error {
100100
var err error
101101
if cf == nil {
102-
cf, err = InitCmdFactory(true)
102+
cf, err = InitCmdFactory(true, true)
103103
if err != nil {
104104
return err
105105
}

peer/chaincode/invoke.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func invokeCmd(cf *ChaincodeCmdFactory) *cobra.Command {
4242
func chaincodeInvoke(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory) error {
4343
var err error
4444
if cf == nil {
45-
cf, err = InitCmdFactory(true)
45+
cf, err = InitCmdFactory(true, true)
4646
if err != nil {
4747
return err
4848
}

0 commit comments

Comments
 (0)