342 lines
12 KiB
Go
342 lines
12 KiB
Go
/*
|
|
Copyright IBM Corp. 2017 All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/hyperledger/fabric-config/protolator"
|
|
"github.com/hyperledger/fabric-config/protolator/protoext/ordererext"
|
|
cb "github.com/hyperledger/fabric-protos-go/common"
|
|
"github.com/hyperledger/fabric/bccsp/factory"
|
|
"github.com/hyperledger/fabric/common/channelconfig"
|
|
"github.com/hyperledger/fabric/common/flogging"
|
|
"github.com/hyperledger/fabric/internal/configtxgen/encoder"
|
|
"github.com/hyperledger/fabric/internal/configtxgen/genesisconfig"
|
|
"github.com/hyperledger/fabric/internal/configtxgen/metadata"
|
|
"github.com/hyperledger/fabric/internal/configtxlator/update"
|
|
"github.com/hyperledger/fabric/protoutil"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var logger = flogging.MustGetLogger("common.tools.configtxgen")
|
|
|
|
func doOutputBlock(config *genesisconfig.Profile, channelID string, outputBlock string) error {
|
|
pgen, err := encoder.NewBootstrapper(config)
|
|
if err != nil {
|
|
return errors.WithMessage(err, "could not create bootstrapper")
|
|
}
|
|
logger.Info("Generating genesis block")
|
|
if config.Orderer == nil {
|
|
return errors.New("refusing to generate block which is missing orderer section")
|
|
}
|
|
if config.Consortiums != nil {
|
|
logger.Error("Warning: 'Consortiums' should be nil since system channel is no longer supported in Fabric v3.x")
|
|
} else {
|
|
if config.Application == nil {
|
|
return errors.New("refusing to generate application channel block which is missing application section")
|
|
}
|
|
logger.Info("Creating application channel genesis block")
|
|
}
|
|
genesisBlock := pgen.GenesisBlockForChannel(channelID)
|
|
logger.Info("Writing genesis block")
|
|
err = writeFile(outputBlock, protoutil.MarshalOrPanic(genesisBlock), 0o640)
|
|
if err != nil {
|
|
return fmt.Errorf("error writing genesis block: %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func doOutputChannelCreateTx(conf, baseProfile *genesisconfig.Profile, channelID string, outputChannelCreateTx string) error {
|
|
logger.Info("Generating new channel configtx")
|
|
|
|
var configtx *cb.Envelope
|
|
var err error
|
|
if baseProfile == nil {
|
|
configtx, err = encoder.MakeChannelCreationTransaction(channelID, nil, conf)
|
|
} else {
|
|
configtx, err = encoder.MakeChannelCreationTransactionWithSystemChannelContext(channelID, nil, conf, baseProfile)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
logger.Info("Writing new channel tx")
|
|
err = writeFile(outputChannelCreateTx, protoutil.MarshalOrPanic(configtx), 0o640)
|
|
if err != nil {
|
|
return fmt.Errorf("error writing channel create tx: %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func doOutputAnchorPeersUpdate(conf *genesisconfig.Profile, channelID string, outputAnchorPeersUpdate string, asOrg string) error {
|
|
logger.Info("Generating anchor peer update")
|
|
if asOrg == "" {
|
|
return fmt.Errorf("must specify an organization to update the anchor peer for")
|
|
}
|
|
|
|
if conf.Application == nil {
|
|
return fmt.Errorf("cannot update anchor peers without an application section")
|
|
}
|
|
|
|
original, err := encoder.NewChannelGroup(conf)
|
|
if err != nil {
|
|
return errors.WithMessage(err, "error parsing profile as channel group")
|
|
}
|
|
original.Groups[channelconfig.ApplicationGroupKey].Version = 1
|
|
|
|
updated := proto.Clone(original).(*cb.ConfigGroup)
|
|
|
|
originalOrg, ok := original.Groups[channelconfig.ApplicationGroupKey].Groups[asOrg]
|
|
if !ok {
|
|
return errors.Errorf("org with name '%s' does not exist in config", asOrg)
|
|
}
|
|
|
|
if _, ok = originalOrg.Values[channelconfig.AnchorPeersKey]; !ok {
|
|
return errors.Errorf("org '%s' does not have any anchor peers defined", asOrg)
|
|
}
|
|
|
|
delete(originalOrg.Values, channelconfig.AnchorPeersKey)
|
|
|
|
updt, err := update.Compute(&cb.Config{ChannelGroup: original}, &cb.Config{ChannelGroup: updated})
|
|
if err != nil {
|
|
return errors.WithMessage(err, "could not compute update")
|
|
}
|
|
updt.ChannelId = channelID
|
|
|
|
newConfigUpdateEnv := &cb.ConfigUpdateEnvelope{
|
|
ConfigUpdate: protoutil.MarshalOrPanic(updt),
|
|
}
|
|
|
|
updateTx, err := protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG_UPDATE, channelID, nil, newConfigUpdateEnv, 0, 0)
|
|
if err != nil {
|
|
return errors.WithMessage(err, "could not create signed envelope")
|
|
}
|
|
|
|
logger.Info("Writing anchor peer update")
|
|
err = writeFile(outputAnchorPeersUpdate, protoutil.MarshalOrPanic(updateTx), 0o640)
|
|
if err != nil {
|
|
return fmt.Errorf("Error writing channel anchor peer update: %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func doInspectBlock(inspectBlock string) error {
|
|
logger.Info("Inspecting block")
|
|
data, err := ioutil.ReadFile(inspectBlock)
|
|
if err != nil {
|
|
return fmt.Errorf("could not read block %s", inspectBlock)
|
|
}
|
|
|
|
logger.Info("Parsing genesis block")
|
|
block, err := protoutil.UnmarshalBlock(data)
|
|
if err != nil {
|
|
return fmt.Errorf("error unmarshalling to block: %s", err)
|
|
}
|
|
err = protolator.DeepMarshalJSON(os.Stdout, block)
|
|
if err != nil {
|
|
return fmt.Errorf("malformed block contents: %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func doInspectChannelCreateTx(inspectChannelCreateTx string) error {
|
|
logger.Info("Inspecting transaction")
|
|
data, err := ioutil.ReadFile(inspectChannelCreateTx)
|
|
if err != nil {
|
|
return fmt.Errorf("could not read channel create tx: %s", err)
|
|
}
|
|
|
|
logger.Info("Parsing transaction")
|
|
env, err := protoutil.UnmarshalEnvelope(data)
|
|
if err != nil {
|
|
return fmt.Errorf("Error unmarshalling envelope: %s", err)
|
|
}
|
|
|
|
err = protolator.DeepMarshalJSON(os.Stdout, env)
|
|
if err != nil {
|
|
return fmt.Errorf("malformed transaction contents: %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func doPrintOrg(t *genesisconfig.TopLevel, printOrg string) error {
|
|
for _, org := range t.Organizations {
|
|
if org.Name == printOrg {
|
|
og, err := encoder.NewOrdererOrgGroup(org)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "bad org definition for org %s", org.Name)
|
|
}
|
|
|
|
if err := protolator.DeepMarshalJSON(os.Stdout, &ordererext.DynamicOrdererOrgGroup{ConfigGroup: og}); err != nil {
|
|
return errors.Wrapf(err, "malformed org definition for org: %s", org.Name)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
return errors.Errorf("organization %s not found", printOrg)
|
|
}
|
|
|
|
func writeFile(filename string, data []byte, perm os.FileMode) error {
|
|
dirPath := filepath.Dir(filename)
|
|
exists, err := dirExists(dirPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exists {
|
|
err = os.MkdirAll(dirPath, 0o750)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return ioutil.WriteFile(filename, data, perm)
|
|
}
|
|
|
|
func dirExists(path string) (bool, error) {
|
|
_, err := os.Stat(path)
|
|
if err == nil {
|
|
return true, nil
|
|
}
|
|
if os.IsNotExist(err) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
|
|
func main() {
|
|
var outputBlock, outputChannelCreateTx, channelCreateTxBaseProfile, profile, configPath, channelID, inspectBlock, inspectChannelCreateTx, outputAnchorPeersUpdate, asOrg, printOrg string
|
|
|
|
flag.StringVar(&outputBlock, "outputBlock", "", "The path to write the genesis block to (if set)")
|
|
flag.StringVar(&channelID, "channelID", "", "The channel ID to use in the configtx")
|
|
flag.StringVar(&outputChannelCreateTx, "outputCreateChannelTx", "", "[DEPRECATED] The path to write a channel creation configtx to (if set)")
|
|
flag.StringVar(&channelCreateTxBaseProfile, "channelCreateTxBaseProfile", "", "[DEPRECATED] Specifies a profile to consider as the orderer system channel current state to allow modification of non-application parameters during channel create tx generation. Only valid in conjunction with 'outputCreateChannelTx'.")
|
|
flag.StringVar(&profile, "profile", "", "The profile from configtx.yaml to use for generation.")
|
|
flag.StringVar(&configPath, "configPath", "", "The path containing the configuration to use (if set)")
|
|
flag.StringVar(&inspectBlock, "inspectBlock", "", "Prints the configuration contained in the block at the specified path")
|
|
flag.StringVar(&inspectChannelCreateTx, "inspectChannelCreateTx", "", "[DEPRECATED] Prints the configuration contained in the transaction at the specified path")
|
|
flag.StringVar(&outputAnchorPeersUpdate, "outputAnchorPeersUpdate", "", "[DEPRECATED] Creates a config update to update an anchor peer (works only with the default channel creation, and only for the first update)")
|
|
flag.StringVar(&asOrg, "asOrg", "", "Performs the config generation as a particular organization (by name), only including values in the write set that org (likely) has privilege to set")
|
|
flag.StringVar(&printOrg, "printOrg", "", "Prints the definition of an organization as JSON. (useful for adding an org to a channel manually)")
|
|
|
|
version := flag.Bool("version", false, "Show version information")
|
|
|
|
flag.Parse()
|
|
|
|
if channelID == "" && (outputBlock != "" || outputChannelCreateTx != "" || outputAnchorPeersUpdate != "") {
|
|
logger.Fatalf("Missing channelID, please specify it with '-channelID'")
|
|
}
|
|
|
|
// show version
|
|
if *version {
|
|
printVersion()
|
|
os.Exit(0)
|
|
}
|
|
|
|
// don't need to panic when running via command line
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
if strings.Contains(fmt.Sprint(err), "Error reading configuration: Unsupported Config Type") {
|
|
logger.Error("Could not find configtx.yaml. " +
|
|
"Please make sure that FABRIC_CFG_PATH or -configPath is set to a path " +
|
|
"which contains configtx.yaml")
|
|
os.Exit(1)
|
|
}
|
|
if strings.Contains(fmt.Sprint(err), "Could not find profile") {
|
|
logger.Error(fmt.Sprint(err) + ". " +
|
|
"Please make sure that FABRIC_CFG_PATH or -configPath is set to a path " +
|
|
"which contains configtx.yaml with the specified profile")
|
|
os.Exit(1)
|
|
}
|
|
logger.Panic(err)
|
|
}
|
|
}()
|
|
|
|
logger.Info("Loading configuration")
|
|
err := factory.InitFactories(nil)
|
|
if err != nil {
|
|
logger.Fatalf("Error on initFactories: %s", err)
|
|
}
|
|
var profileConfig *genesisconfig.Profile
|
|
if outputBlock != "" || outputChannelCreateTx != "" || outputAnchorPeersUpdate != "" {
|
|
if profile == "" {
|
|
logger.Fatalf("The '-profile' is required when '-outputBlock', '-outputChannelCreateTx', or '-outputAnchorPeersUpdate' is specified")
|
|
}
|
|
|
|
if configPath != "" {
|
|
profileConfig = genesisconfig.Load(profile, configPath)
|
|
} else {
|
|
profileConfig = genesisconfig.Load(profile)
|
|
}
|
|
}
|
|
|
|
var baseProfile *genesisconfig.Profile
|
|
if channelCreateTxBaseProfile != "" {
|
|
if outputChannelCreateTx == "" {
|
|
logger.Warning("Specified 'channelCreateTxBaseProfile', but did not specify 'outputChannelCreateTx', 'channelCreateTxBaseProfile' will not affect output.")
|
|
}
|
|
if configPath != "" {
|
|
baseProfile = genesisconfig.Load(channelCreateTxBaseProfile, configPath)
|
|
} else {
|
|
baseProfile = genesisconfig.Load(channelCreateTxBaseProfile)
|
|
}
|
|
}
|
|
|
|
if outputBlock != "" {
|
|
if err := doOutputBlock(profileConfig, channelID, outputBlock); err != nil {
|
|
logger.Fatalf("Error on outputBlock: %s", err)
|
|
}
|
|
}
|
|
|
|
if outputChannelCreateTx != "" {
|
|
if err := doOutputChannelCreateTx(profileConfig, baseProfile, channelID, outputChannelCreateTx); err != nil {
|
|
logger.Fatalf("Error on outputChannelCreateTx: %s", err)
|
|
}
|
|
}
|
|
|
|
if inspectBlock != "" {
|
|
if err := doInspectBlock(inspectBlock); err != nil {
|
|
logger.Fatalf("Error on inspectBlock: %s", err)
|
|
}
|
|
}
|
|
|
|
if inspectChannelCreateTx != "" {
|
|
if err := doInspectChannelCreateTx(inspectChannelCreateTx); err != nil {
|
|
logger.Fatalf("Error on inspectChannelCreateTx: %s", err)
|
|
}
|
|
}
|
|
|
|
if outputAnchorPeersUpdate != "" {
|
|
if err := doOutputAnchorPeersUpdate(profileConfig, channelID, outputAnchorPeersUpdate, asOrg); err != nil {
|
|
logger.Fatalf("Error on inspectChannelCreateTx: %s", err)
|
|
}
|
|
}
|
|
|
|
if printOrg != "" {
|
|
var topLevelConfig *genesisconfig.TopLevel
|
|
if configPath != "" {
|
|
topLevelConfig = genesisconfig.LoadTopLevel(configPath)
|
|
} else {
|
|
topLevelConfig = genesisconfig.LoadTopLevel()
|
|
}
|
|
|
|
if err := doPrintOrg(topLevelConfig, printOrg); err != nil {
|
|
logger.Fatalf("Error on printOrg: %s", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func printVersion() {
|
|
fmt.Println(metadata.GetVersionInfo())
|
|
}
|