Skip to content

Commit 215126c

Browse files
author
Srinivasan Muralidharan
committed
FAB-2417 create channel from provided config tx
https://jira.hyperledger.org/browse/FAB-2417 CLI generates transaction based on default config (mainly msp/sampleconfig with DEFAULT id). Now that the `configtxgen` tool is available, we can use more natural workflow to generate desired channel transaction. Assuming such a transaction called `mychannel.tx` is generated using `configtxgen` tool for channel `mychannel` the following command will consume and submit that output to the orderer to create a channel peer channel create -c mychannel -f /path/to/mychannel.tx The `peer channel create -c mychannel` will continue to work to create channel with default config. Change-Id: I87538323af0300cf809810fd0900ad4d32409258 Signed-off-by: Srinivasan Muralidharan <[email protected]>
1 parent 7c1934a commit 215126c

File tree

3 files changed

+232
-6
lines changed

3 files changed

+232
-6
lines changed

peer/channel/channel.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ var (
4141
// create related variables
4242
chainID string
4343
anchorPeerList string
44+
channelTxFile string
4445
)
4546

4647
// Cmd returns the cobra command for Node
@@ -64,8 +65,9 @@ func AddFlags(cmd *cobra.Command) {
6465
flags := cmd.PersistentFlags()
6566

6667
flags.StringVarP(&genesisBlockPath, "blockpath", "b", common.UndefinedParamValue, "Path to file containing genesis block")
67-
flags.StringVarP(&chainID, "chain", "c", "mychain", "In case of a newChain command, the chain ID to create.")
68+
flags.StringVarP(&chainID, "chain", "c", common.UndefinedParamValue, "In case of a newChain command, the chain ID to create.")
6869
flags.StringVarP(&anchorPeerList, "anchors", "a", "", anchorPeerUsage)
70+
flags.StringVarP(&channelTxFile, "file", "f", "", "Configuration transaction file generated by a tool such as configtxgen for submitting to orderer")
6971
}
7072

7173
var channelCmd = &cobra.Command{

peer/channel/create.go

+78-5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package channel
1818

1919
import (
20+
"fmt"
2021
"io/ioutil"
2122
"time"
2223

@@ -28,9 +29,24 @@ import (
2829
mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
2930
"github.com/hyperledger/fabric/peer/common"
3031
cb "github.com/hyperledger/fabric/protos/common"
32+
"github.com/hyperledger/fabric/protos/utils"
3133
"github.com/spf13/cobra"
3234
)
3335

36+
//ConfigTxFileNotFound channel create configuration tx file not found
37+
type ConfigTxFileNotFound string
38+
39+
func (e ConfigTxFileNotFound) Error() string {
40+
return fmt.Sprintf("channel create configuration tx file not found %s", string(e))
41+
}
42+
43+
//InvalidCreateTx invalid channel create transaction
44+
type InvalidCreateTx string
45+
46+
func (e InvalidCreateTx) Error() string {
47+
return fmt.Sprintf("Invalid channel create transaction : %s", string(e))
48+
}
49+
3450
func createCmd(cf *ChannelCmdFactory) *cobra.Command {
3551
createCmd := &cobra.Command{
3652
Use: "create",
@@ -44,15 +60,16 @@ func createCmd(cf *ChannelCmdFactory) *cobra.Command {
4460
return createCmd
4561
}
4662

47-
func sendCreateChainTransaction(cf *ChannelCmdFactory) error {
63+
func createChannelFromDefaults(cf *ChannelCmdFactory) (*cb.Envelope, error) {
4864
if cf.AnchorPeerParser == nil {
4965
cf.AnchorPeerParser = common.GetDefaultAnchorPeerParser()
5066
}
67+
5168
anchorPeers, err := cf.AnchorPeerParser.Parse()
5269
if err != nil {
53-
return err
70+
return nil, err
5471
}
55-
//TODO this is a temporary hack until `orderer.template` and 'msp.template' is supplied from the CLI
72+
5673
oTemplate := configtxtest.OrdererTemplate()
5774
oOrgTemplate := configtxtest.OrdererOrgTemplate()
5875
appOrgTemplate := configtxtest.ApplicationOrgTemplate()
@@ -61,13 +78,64 @@ func sendCreateChainTransaction(cf *ChannelCmdFactory) error {
6178

6279
signer, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()
6380
if err != nil {
64-
return err
81+
return nil, err
6582
}
6683

6784
chCrtEnv, err := configtx.MakeChainCreationTransaction(provisional.AcceptAllPolicyKey, chainID, signer, chCrtTemp)
6885

6986
if err != nil {
70-
return err
87+
return nil, err
88+
}
89+
90+
return chCrtEnv, nil
91+
}
92+
93+
func createChannelFromConfigTx(configTxFileName string) (*cb.Envelope, error) {
94+
cftx, err := ioutil.ReadFile(configTxFileName)
95+
if err != nil {
96+
return nil, ConfigTxFileNotFound(err.Error())
97+
}
98+
99+
env := utils.UnmarshalEnvelopeOrPanic(cftx)
100+
101+
payload := utils.ExtractPayloadOrPanic(env)
102+
103+
if payload.Header == nil || payload.Header.ChannelHeader == nil {
104+
return nil, InvalidCreateTx("bad header")
105+
}
106+
107+
ch, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
108+
if err != nil {
109+
return nil, InvalidCreateTx("could not unmarshall channel header")
110+
}
111+
112+
if ch.Type != int32(cb.HeaderType_CONFIG_UPDATE) {
113+
return nil, InvalidCreateTx("bad type")
114+
}
115+
116+
if ch.ChannelId == "" {
117+
return nil, InvalidCreateTx("empty channel id")
118+
}
119+
120+
if ch.ChannelId != chainID {
121+
return nil, InvalidCreateTx(fmt.Sprintf("mismatched channel ID %s != %s", ch.ChannelId, chainID))
122+
}
123+
124+
return env, nil
125+
}
126+
127+
func sendCreateChainTransaction(cf *ChannelCmdFactory) error {
128+
var err error
129+
var chCrtEnv *cb.Envelope
130+
131+
if channelTxFile != "" {
132+
if chCrtEnv, err = createChannelFromConfigTx(channelTxFile); err != nil {
133+
return err
134+
}
135+
} else {
136+
if chCrtEnv, err = createChannelFromDefaults(cf); err != nil {
137+
return err
138+
}
71139
}
72140

73141
err = cf.BroadcastClient.Send(chCrtEnv)
@@ -105,6 +173,11 @@ func executeCreate(cf *ChannelCmdFactory) error {
105173
}
106174

107175
func create(cmd *cobra.Command, args []string, cf *ChannelCmdFactory) error {
176+
//the global chainID filled by the "-c" command
177+
if chainID == common.UndefinedParamValue {
178+
return fmt.Errorf("Must supply channel ID .\n")
179+
}
180+
108181
var err error
109182
if cf == nil {
110183
cf, err = InitCmdFactory(false)

peer/channel/create_test.go

+151
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ package channel
1818

1919
import (
2020
"fmt"
21+
"io/ioutil"
2122
"os"
23+
"path/filepath"
2224
"sync"
2325
"testing"
2426

27+
"github.com/golang/protobuf/proto"
28+
2529
"github.com/hyperledger/fabric/msp/mgmt/testtools"
2630
"github.com/hyperledger/fabric/peer/common"
2731
cb "github.com/hyperledger/fabric/protos/common"
@@ -251,3 +255,150 @@ func TestCreateChainDeliverFail(t *testing.T) {
251255
}
252256
}
253257
}
258+
259+
func createTxFile(filename string, typ cb.HeaderType, channelID string) (*cb.Envelope, error) {
260+
ch := &cb.ChannelHeader{Type: int32(typ), ChannelId: channelID}
261+
data, err := proto.Marshal(ch)
262+
if err != nil {
263+
return nil, err
264+
}
265+
266+
p := &cb.Payload{Header: &cb.Header{ChannelHeader: data}}
267+
data, err = proto.Marshal(p)
268+
if err != nil {
269+
return nil, err
270+
}
271+
272+
env := &cb.Envelope{Payload: data}
273+
data, err = proto.Marshal(env)
274+
if err != nil {
275+
return nil, err
276+
}
277+
278+
if err = ioutil.WriteFile(filename, data, 0644); err != nil {
279+
return nil, err
280+
}
281+
282+
return env, nil
283+
}
284+
285+
func TestCreateChainFromTx(t *testing.T) {
286+
InitMSP()
287+
288+
mockchannel := "mockchannel"
289+
290+
dir, err := ioutil.TempDir("/tmp", "createtestfromtx-")
291+
if err != nil {
292+
t.Fatalf("couldn't create temp dir")
293+
}
294+
295+
defer os.RemoveAll(dir) // clean up
296+
297+
//this could be created by the create command
298+
defer os.Remove(mockchannel + ".block")
299+
300+
file := filepath.Join(dir, mockchannel)
301+
302+
signer, err := common.GetDefaultSigner()
303+
if err != nil {
304+
t.Fatalf("Get default signer error: %v", err)
305+
}
306+
307+
mockBroadcastClient := common.GetMockBroadcastClient(nil)
308+
309+
mockCF := &ChannelCmdFactory{
310+
BroadcastClient: mockBroadcastClient,
311+
Signer: signer,
312+
DeliverClient: &mockDeliverClient{},
313+
AnchorPeerParser: common.GetAnchorPeersParser("../common/testdata/anchorPeersOrg1.txt"),
314+
}
315+
316+
cmd := createCmd(mockCF)
317+
318+
AddFlags(cmd)
319+
320+
args := []string{"-c", mockchannel, "-f", file, "-a", "../common/testdata/anchorPeersOrg1.txt"}
321+
cmd.SetArgs(args)
322+
323+
if _, err = createTxFile(file, cb.HeaderType_CONFIG_UPDATE, mockchannel); err != nil {
324+
t.Fatalf("couldn't create tx file")
325+
}
326+
327+
if err := cmd.Execute(); err != nil {
328+
t.Errorf("create chain failed")
329+
}
330+
}
331+
332+
func TestCreateChainInvalidTx(t *testing.T) {
333+
InitMSP()
334+
335+
mockchannel := "mockchannel"
336+
337+
dir, err := ioutil.TempDir("/tmp", "createinvaltest-")
338+
if err != nil {
339+
t.Fatalf("couldn't create temp dir")
340+
}
341+
342+
defer os.RemoveAll(dir) // clean up
343+
344+
//this is created by create command
345+
defer os.Remove(mockchannel + ".block")
346+
347+
file := filepath.Join(dir, mockchannel)
348+
349+
signer, err := common.GetDefaultSigner()
350+
if err != nil {
351+
t.Fatalf("Get default signer error: %v", err)
352+
}
353+
354+
mockBroadcastClient := common.GetMockBroadcastClient(nil)
355+
356+
mockCF := &ChannelCmdFactory{
357+
BroadcastClient: mockBroadcastClient,
358+
Signer: signer,
359+
DeliverClient: &mockDeliverClient{},
360+
AnchorPeerParser: common.GetAnchorPeersParser("../common/testdata/anchorPeersOrg1.txt"),
361+
}
362+
363+
cmd := createCmd(mockCF)
364+
365+
AddFlags(cmd)
366+
367+
args := []string{"-c", mockchannel, "-f", file, "-a", "../common/testdata/anchorPeersOrg1.txt"}
368+
cmd.SetArgs(args)
369+
370+
//bad type CONFIG
371+
if _, err = createTxFile(file, cb.HeaderType_CONFIG, mockchannel); err != nil {
372+
t.Fatalf("couldn't create tx file")
373+
}
374+
375+
defer os.Remove(file)
376+
377+
if err := cmd.Execute(); err == nil {
378+
t.Errorf("expected error")
379+
} else if _, ok := err.(InvalidCreateTx); !ok {
380+
t.Errorf("invalid error")
381+
}
382+
383+
//bad channel name - does not match one specified in command
384+
if _, err = createTxFile(file, cb.HeaderType_CONFIG_UPDATE, "different_channel"); err != nil {
385+
t.Fatalf("couldn't create tx file")
386+
}
387+
388+
if err := cmd.Execute(); err == nil {
389+
t.Errorf("expected error")
390+
} else if _, ok := err.(InvalidCreateTx); !ok {
391+
t.Errorf("invalid error")
392+
}
393+
394+
//empty channel
395+
if _, err = createTxFile(file, cb.HeaderType_CONFIG_UPDATE, ""); err != nil {
396+
t.Fatalf("couldn't create tx file")
397+
}
398+
399+
if err := cmd.Execute(); err == nil {
400+
t.Errorf("expected error")
401+
} else if _, ok := err.(InvalidCreateTx); !ok {
402+
t.Errorf("invalid error")
403+
}
404+
}

0 commit comments

Comments
 (0)