Skip to content

Commit

Permalink
[FAB-5441] Add multisig support to peer CLI
Browse files Browse the repository at this point in the history
Without utilizing the SDKs, it is currently not possible to submit a
configtx with signatures from multiple parties.  This makes it very
difficult if not impossible to modify things like channel membership.

This CR adds a new command to the peer channel CLI: signconfigtx

This command operates like the update command, but instead of signing
and submitting it to ordering, it simply signs the transaction, then
writes it back to disk.  This write is done in place, replacing the
input file.

Change-Id: I59bd922ec6ecaca11d8e23ef604228e51c229b46
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Jul 26, 2017
1 parent c904d2b commit 231460c
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 0 deletions.
1 change: 1 addition & 0 deletions peer/channel/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func Cmd(cf *ChannelCmdFactory) *cobra.Command {
channelCmd.AddCommand(joinCmd(cf))
channelCmd.AddCommand(listCmd(cf))
channelCmd.AddCommand(updateCmd(cf))
channelCmd.AddCommand(signconfigtxCmd(cf))

return channelCmd
}
Expand Down
6 changes: 6 additions & 0 deletions peer/channel/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ func sanityCheckAndSignConfigTx(envConfigUpdate *cb.Envelope) (*cb.Envelope, err
return nil, InvalidCreateTx("empty channel id")
}

// Specifying the chainID on the CLI is usually redundant, as a hack, set it
// here if it has not been set explicitly
if chainID == "" {
chainID = ch.ChannelId
}

if ch.ChannelId != chainID {
return nil, InvalidCreateTx(fmt.Sprintf("mismatched channel ID %s != %s", ch.ChannelId, chainID))
}
Expand Down
65 changes: 65 additions & 0 deletions peer/channel/signconfigtx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package channel

import (
"io/ioutil"

"github.com/hyperledger/fabric/protos/utils"

"github.com/spf13/cobra"
)

func signconfigtxCmd(cf *ChannelCmdFactory) *cobra.Command {
signconfigtxCmd := &cobra.Command{
Use: "signconfigtx",
Short: "Signs a configtx update.",
Long: "Signs the supplied configtx update file in place on the filesystem. Requires '-f'.",
RunE: func(cmd *cobra.Command, args []string) error {
return sign(cmd, args, cf)
},
}
flagList := []string{
"file",
}
attachFlags(signconfigtxCmd, flagList)

return signconfigtxCmd
}

func sign(cmd *cobra.Command, args []string, cf *ChannelCmdFactory) error {
if channelTxFile == "" {
return InvalidCreateTx("No configtx file name supplied")
}

var err error
if cf == nil {
cf, err = InitCmdFactory(EndorserNotRequired, OrdererNotRequired)
if err != nil {
return err
}
}

fileData, err := ioutil.ReadFile(channelTxFile)
if err != nil {
return ConfigTxFileNotFound(err.Error())
}

ctxEnv, err := utils.UnmarshalEnvelope(fileData)
if err != nil {
return err
}

sCtxEnv, err := sanityCheckAndSignConfigTx(ctxEnv)
if err != nil {
return err
}

sCtxEnvData := utils.MarshalOrPanic(sCtxEnv)

return ioutil.WriteFile(channelTxFile, sCtxEnvData, 0660)
}
98 changes: 98 additions & 0 deletions peer/channel/signconfigtx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package channel

import (
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/hyperledger/fabric/peer/common"
cb "github.com/hyperledger/fabric/protos/common"

"github.com/stretchr/testify/assert"
)

func TestSignConfigtx(t *testing.T) {
InitMSP()
resetFlags()

dir, err := ioutil.TempDir("/tmp", "signconfigtxtest-")
if err != nil {
t.Fatalf("couldn't create temp dir")
}
defer os.RemoveAll(dir) // clean up

configtxFile := filepath.Join(dir, mockChannel)
if _, err = createTxFile(configtxFile, cb.HeaderType_CONFIG_UPDATE, mockChannel); err != nil {
t.Fatalf("couldn't create tx file")
}

signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}

mockCF := &ChannelCmdFactory{
Signer: signer,
}

cmd := signconfigtxCmd(mockCF)

AddFlags(cmd)

args := []string{"-f", configtxFile}
cmd.SetArgs(args)

assert.NoError(t, cmd.Execute())
}

func TestSignConfigtxMissingConfigTxFlag(t *testing.T) {
InitMSP()
resetFlags()

signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}

mockCF := &ChannelCmdFactory{
Signer: signer,
}

cmd := signconfigtxCmd(mockCF)

AddFlags(cmd)

cmd.SetArgs([]string{})

assert.Error(t, cmd.Execute())
}

func TestSignConfigtxChannelMissingConfigTxFile(t *testing.T) {
InitMSP()
resetFlags()

signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}

mockCF := &ChannelCmdFactory{
Signer: signer,
}

cmd := signconfigtxCmd(mockCF)

AddFlags(cmd)

args := []string{"-f", "Non-existant"}
cmd.SetArgs(args)

assert.Error(t, cmd.Execute())
}

0 comments on commit 231460c

Please sign in to comment.