Skip to content

Commit

Permalink
[FAB-2584] configtxgen prints block config as json
Browse files Browse the repository at this point in the history
https://jira.hyperledger.org/browse/FAB-2584

This CR leverages the work from FAB-2577 to allow the configtxgen tool
to dump the configuration inside a configuration block to the screen as
JSON.  This allows for the inspection of the genesis material.

Change-Id: I863286b88f456adf7a7958af86306de148991817
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Mar 7, 2017
1 parent 86f65d3 commit d4a11db
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 23 deletions.
8 changes: 8 additions & 0 deletions common/configtx/config.go
Expand Up @@ -29,6 +29,14 @@ import (
"github.com/golang/protobuf/proto"
)

type ConfigResult interface {
JSON() string
}

func NewConfigResult(config *cb.ConfigGroup, proposer api.Proposer) (ConfigResult, error) {
return processConfig(config, proposer)
}

type configResult struct {
tx interface{}
groupName string
Expand Down
121 changes: 98 additions & 23 deletions common/configtx/tool/configtxgen/main.go
Expand Up @@ -17,28 +17,118 @@ limitations under the License.
package main

import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io/ioutil"

"github.com/hyperledger/fabric/bccsp/factory"
"github.com/hyperledger/fabric/common/configtx"
genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig"
"github.com/hyperledger/fabric/common/configtx/tool/provisional"
"github.com/hyperledger/fabric/msp"
cb "github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/utils"

"github.com/golang/protobuf/proto"
logging "github.com/op/go-logging"
)

var logger = logging.MustGetLogger("common/configtx/tool")

func doOutputBlock(pgen provisional.Generator, channelID string, outputBlock string) {
logger.Info("Generating genesis block")
genesisBlock := pgen.GenesisBlockForChannel(channelID)
logger.Info("Writing genesis block")
err := ioutil.WriteFile(outputBlock, utils.MarshalOrPanic(genesisBlock), 0644)
if err != nil {
logger.Errorf("Error writing genesis block: %s", err)
}
}

func doOutputChannelCreateTx(pgen provisional.Generator, channelID string, outputChannelCreateTx string) {
logger.Info("Generating new channel configtx")
// TODO, use actual MSP eventually
signer, err := msp.NewNoopMsp().GetDefaultSigningIdentity()
if err != nil {
logger.Fatalf("Error getting signing identity: %s", err)
}
configtx, err := configtx.MakeChainCreationTransaction(provisional.AcceptAllPolicyKey, channelID, signer, pgen.ChannelTemplate())
if err != nil {
logger.Fatalf("Error generating configtx: %s", err)
}
logger.Info("Writing new channel tx")
err = ioutil.WriteFile(outputChannelCreateTx, utils.MarshalOrPanic(configtx), 0644)
if err != nil {
logger.Errorf("Error writing channel create tx: %s", err)
}
}

func doInspectBlock(inspectBlock string) {
logger.Info("Inspecting block")
data, err := ioutil.ReadFile(inspectBlock)
logger.Info("Parsing genesis block")
block := &cb.Block{}
err = proto.Unmarshal(data, block)
if err != nil {
logger.Fatalf("Error unmarshaling block: %s", err)
}

ctx, err := utils.ExtractEnvelope(block, 0)
if err != nil {
logger.Fatalf("Error retrieving configtx from block: %s", err)
}

payload, err := utils.UnmarshalPayload(ctx.Payload)
if err != nil {
logger.Fatalf("Error extracting configtx payload: %s", err)
}

if payload.Header == nil {
logger.Fatalf("Config block did not contain header")
}

header, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil {
logger.Fatalf("Error unmarshaling channel header: %s", err)
}

if header.Type != int32(cb.HeaderType_CONFIG) {
logger.Fatalf("Bad header type: %d", header.Type)
}

configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data)
if err != nil {
logger.Fatalf("Bad configuration envelope")
}

if configEnvelope.Config == nil {
logger.Fatalf("ConfigEnvelope contained no config")
}

configResult, err := configtx.NewConfigResult(configEnvelope.Config.ChannelGroup, configtx.NewInitializer())
if err != nil {
logger.Fatalf("Error parsing configuration: %s", err)
}

buffer := &bytes.Buffer{}
json.Indent(buffer, []byte(configResult.JSON()), "", " ")

fmt.Printf("Config for channel: %s at sequence %d\n", header.ChannelId, configEnvelope.Config.Sequence)

fmt.Println(buffer.String())
}

func main() {
var outputBlock, outputChannelCreateTx, profile, channelID string
var outputBlock, outputChannelCreateTx, profile, channelID, inspectBlock string

flag.StringVar(&outputBlock, "outputBlock", "", "The path to write the genesis block to (if set)")
flag.StringVar(&channelID, "channelID", provisional.TestChainID, "The channel ID to use in the configtx")
flag.StringVar(&outputChannelCreateTx, "outputCreateChannelTx", "", "The path to write a channel creation configtx to (if set)")
flag.StringVar(&profile, "profile", genesisconfig.SampleInsecureProfile, "The profile from configtx.yaml to use for generation.")
flag.StringVar(&inspectBlock, "inspectBlock", "", "Prints the configuration contained in the block at the specified path")

flag.Parse()

logging.SetLevel(logging.INFO, "")
Expand All @@ -49,30 +139,15 @@ func main() {
pgen := provisional.New(config)

if outputBlock != "" {
logger.Info("Generating genesis block")
genesisBlock := pgen.GenesisBlock()
logger.Info("Writing genesis block")
err := ioutil.WriteFile(outputBlock, utils.MarshalOrPanic(genesisBlock), 0644)
if err != nil {
logger.Errorf("Error writing genesis block: %s", err)
}
doOutputBlock(pgen, channelID, outputBlock)
}

if outputChannelCreateTx != "" {
logger.Info("Generating new channel configtx")
// TODO, use actual MSP eventually
signer, err := msp.NewNoopMsp().GetDefaultSigningIdentity()
if err != nil {
logger.Fatalf("Error getting signing identity: %s", err)
}
configtx, err := configtx.MakeChainCreationTransaction(provisional.AcceptAllPolicyKey, channelID, signer, pgen.ChannelTemplate())
if err != nil {
logger.Fatalf("Error generating configtx: %s", err)
}
logger.Info("Writing new channel tx")
err = ioutil.WriteFile(outputChannelCreateTx, utils.MarshalOrPanic(configtx), 0644)
if err != nil {
logger.Errorf("Error writing channel create tx: %s", err)
}
doOutputChannelCreateTx(pgen, channelID, outputChannelCreateTx)
}

if inspectBlock != "" {
doInspectBlock(inspectBlock)
}

}
2 changes: 2 additions & 0 deletions common/configtx/tool/provisional/provisional.go
Expand Up @@ -43,6 +43,8 @@ type Generator interface {

// ChannelTemplate returns a template which can be used to help initialize a channel
ChannelTemplate() configtx.Template

GenesisBlockForChannel(channelID string) *cb.Block
}

const (
Expand Down
153 changes: 153 additions & 0 deletions docs/configtxgen.md
@@ -0,0 +1,153 @@
# Configuring using the configtxgen tool

This document describe the usage for the `configtxgen` utility for manipulating fabric channel configuration.

For now, the tool is primarily focused on generating the genesis block for bootstrapping the orderer, but it is intended to be enhanced in the future for generating new channel configurations as well as reconfiguring existing channels.

## Building the tool

Building the tool is as simple as `make configtxgen`. This will create a `configtxgen` binary at `build/bin/configtxgen` which is included in the Vagrant development environment path by default.

## Configuration Profiles

The configuration parameters supplied to the `configtxgen` tool are primarily provided by the `configtx.yaml` file. This file is located at `fabric/common/configtx/tool/configtx.yaml` in the fabric.git repository.

This configuration file is split primarily into three pieces.

1. The `Profiles` section. By default, this section includes some sample configurations which can be used for development or testing scenarios, and refer to crypto material present in the fabric.git tree. These profiles can make a good starting point for construction a real deployment profile. The `configtxgen` tool allows you to specify the profile it is operating under by passing the `-profile` flag. Profiles may explicitly declare all configuration, but usually inherit configuration from the defaults in (3) below.
2. The `Organizations` section. By default, this section includes a single reference to the sampleconfig MSP definition. For production deployments, the sample organization should be removed, and the MSP definitions of the network members should be referenced and defined instead. Each element in the `Organizations` section should be tagged with an anchor label such as `&orgName` which will allow the definition to be referenced in the `Profiles` sections.
3. The default sections. There are default sections for `Orderer` and `Application` configuration, these include attributes like `BatchTimeout` and are generally used as the base inherited values for the profiles.

This configuration file may be edited, or, individual properties may be overridden by setting environment variables, such as `CONFIGTX_ORDERER_ORDERERTYPE=kafka`. Note that the `Profiles` element and profile name do not need to be specified.

## Bootstrapping the orderer
After creating a configuration profile as desired, simply invoke
```
configtxgen -profile <profile_name> -outputBlock <genesis.blockname>
```
This will produce a `genesis.block` file in the current directory. If you wish to skip writing the file simply do not pass `outputBlock`

Then, to utilize this genesis block, before starting the orderer, simply specify `ORDERER_GENERAL_GENESISMETHOD=file` and `ORDERER_GENERAL_GENESISFILE=$PWD/genesis.block` or modify the `orderer.yaml` file to encode these values.

## Creating a channel

The tool can also output a channel creation tx by executing

```
configtxgen -profile <profile_name> -outputCreateChannelTx <output.txname>
```

This will output a marshaled `Envelope` message which may be sent to broadcast to create a channel.

## Reviewing a configuration block

In addition to creating configuration, the `configtxgen` tool is also capable of inspecting configuration. You may even wish to combine the inspection with generation. For example:

```
$ build/bin/configtxgen -channelID foo -outputBlock foo.block -inspectBlock foo.block
2017/03/01 21:24:24 Loading configuration
2017/03/01 21:24:24 Checking for configtx.yaml at:
2017/03/01 21:24:24 Checking for configtx.yaml at:
2017/03/01 21:24:24 Checking for configtx.yaml at: /home/yellickj/go/src/github.com/hyperledger/fabric/common/configtx/tool
2017/03/01 21:24:24 map[orderer:map[BatchSize:map[MaxMessageCount:10 AbsoluteMaxBytes:99 MB PreferredMaxBytes:512 KB] Kafka:map[Brokers:[127.0.0.1:9092]] Organizations:<nil> OrdererType:solo Addresses:[127.0.0.1:7050] BatchTimeout:10s] application:map[Organizations:<nil>] profiles:map[SampleInsecureSolo:map[Orderer:map[BatchTimeout:10s BatchSize:map[MaxMessageCount:10 AbsoluteMaxBytes:99 MB PreferredMaxBytes:512 KB] Kafka:map[Brokers:[127.0.0.1:9092]] Organizations:<nil> OrdererType:solo Addresses:[127.0.0.1:7050]] Application:map[Organizations:<nil>]] SampleInsecureKafka:map[Orderer:map[Addresses:[127.0.0.1:7050] BatchTimeout:10s BatchSize:map[AbsoluteMaxBytes:99 MB PreferredMaxBytes:512 KB MaxMessageCount:10] Kafka:map[Brokers:[127.0.0.1:9092]] Organizations:<nil> OrdererType:kafka] Application:map[Organizations:<nil>]] SampleSingleMSPSolo:map[Orderer:map[OrdererType:solo Addresses:[127.0.0.1:7050] BatchTimeout:10s BatchSize:map[MaxMessageCount:10 AbsoluteMaxBytes:99 MB PreferredMaxBytes:512 KB] Kafka:map[Brokers:[127.0.0.1:9092]] Organizations:[map[Name:SampleOrg ID:DEFAULT MSPDir:msp/sampleconfig BCCSP:map[Default:SW SW:map[Hash:SHA3 Security:256 FileKeyStore:map[KeyStore:<nil>]]] AnchorPeers:[map[Host:127.0.0.1 Port:7051]]]]] Application:map[Organizations:[map[Name:SampleOrg ID:DEFAULT MSPDir:msp/sampleconfig BCCSP:map[Default:SW SW:map[Hash:SHA3 Security:256 FileKeyStore:map[KeyStore:<nil>]]] AnchorPeers:[map[Port:7051 Host:127.0.0.1]]]]]]] organizations:[map[Name:SampleOrg ID:DEFAULT MSPDir:msp/sampleconfig BCCSP:map[Default:SW SW:map[Hash:SHA3 Security:256 FileKeyStore:map[KeyStore:<nil>]]] AnchorPeers:[map[Host:127.0.0.1 Port:7051]]]]]
2017/03/01 21:24:24 Generating genesis block
2017/03/01 21:24:24 Writing genesis block
2017/03/01 21:24:24 Inspecting block
2017/03/01 21:24:24 Parsing genesis block
Config for channel: foo
{
"": {
"Values": {},
"Groups": {
"/Channel": {
"Values": {
"HashingAlgorithm": {
"Version": "0",
"ModPolicy": "",
"Value": {
"name": "SHA256"
}
},
"BlockDataHashingStructure": {
"Version": "0",
"ModPolicy": "",
"Value": {
"width": 4294967295
}
},
"OrdererAddresses": {
"Version": "0",
"ModPolicy": "",
"Value": {
"addresses": [
"127.0.0.1:7050"
]
}
}
},
"Groups": {
"/Channel/Orderer": {
"Values": {
"ChainCreationPolicyNames": {
"Version": "0",
"ModPolicy": "",
"Value": {
"names": [
"AcceptAllPolicy"
]
}
},
"ConsensusType": {
"Version": "0",
"ModPolicy": "",
"Value": {
"type": "solo"
}
},
"BatchSize": {
"Version": "0",
"ModPolicy": "",
"Value": {
"maxMessageCount": 10,
"absoluteMaxBytes": 103809024,
"preferredMaxBytes": 524288
}
},
"BatchTimeout": {
"Version": "0",
"ModPolicy": "",
"Value": {
"timeout": "10s"
}
},
"IngressPolicyNames": {
"Version": "0",
"ModPolicy": "",
"Value": {
"names": [
"AcceptAllPolicy"
]
}
},
"EgressPolicyNames": {
"Version": "0",
"ModPolicy": "",
"Value": {
"names": [
"AcceptAllPolicy"
]
}
}
},
"Groups": {}
},
"/Channel/Application": {
"Values": {},
"Groups": {}
}
}
}
}
}
}
```

0 comments on commit d4a11db

Please sign in to comment.